Add API to control mock media devices
authoryouenn@apple.com <youenn@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 25 Jun 2018 19:19:06 +0000 (19:19 +0000)
committeryouenn@apple.com <youenn@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 25 Jun 2018 19:19:06 +0000 (19:19 +0000)
https://bugs.webkit.org/show_bug.cgi?id=186958

Reviewed by Eric Carlson.

Source/WebCore:

Refactor code to introduce a MockDevice structure that can be used in multiple processes.
Update mock sources and center to use MockDevice.
Add API to update mock media devices.

Make MediaDevices an ActiveDOMObject so that it does not get collected when ondevicechange is set.

 Test: fast/mediastream/device-change-event-2.html

* Modules/mediastream/MediaDevices.cpp:
(WebCore::MediaDevices::MediaDevices):
(WebCore::MediaDevices::stop):
(WebCore::MediaDevices::scheduledEventTimerFired):
(WebCore::MediaDevices::hasPendingActivity const):
(WebCore::MediaDevices::activeDOMObjectName const):
(WebCore::MediaDevices::canSuspendForDocumentSuspension const):
* Modules/mediastream/MediaDevices.h:
* Modules/mediastream/MediaDevices.idl:
* WebCore.xcodeproj/project.pbxproj:
* platform/mediastream/RealtimeMediaSourceCenter.h:
* platform/mock/MockMediaDevice.h: Added.
(WebCore::MockMicrophoneProperties::encode const):
(WebCore::MockMicrophoneProperties::decode):
(WebCore::MockCameraProperties::encode const):
(WebCore::MockCameraProperties::decode):
(WebCore::MockDisplayProperties::encode const):
(WebCore::MockDisplayProperties::decode):
(WebCore::MockMediaDevice::isMicrophone const):
(WebCore::MockMediaDevice::isCamera const):
(WebCore::MockMediaDevice::isDisplay const):
(WebCore::MockMediaDevice::type const):
(WebCore::MockMediaDevice::encode const):
(WebCore::MockMediaDevice::decodeMockMediaDevice):
(WebCore::MockMediaDevice::decode):
* platform/mock/MockRealtimeAudioSource.cpp:
(WebCore::MockRealtimeAudioSource::startProducingData):
* platform/mock/MockRealtimeMediaSource.cpp:
(WebCore::defaultDevices):
(WebCore::devices):
(WebCore::deviceMap):
(WebCore::deviceListForDevice):
(WebCore::MockRealtimeMediaSource::createCaptureDevice):
(WebCore::MockRealtimeMediaSource::resetDevices):
(WebCore::MockRealtimeMediaSource::setDevices):
(WebCore::MockRealtimeMediaSource::addDevice):
(WebCore::MockRealtimeMediaSource::removeDevice):
(WebCore::MockRealtimeMediaSource::captureDeviceWithPersistentID):
(WebCore::MockRealtimeMediaSource::audioDevices):
(WebCore::MockRealtimeMediaSource::videoDevices):
(WebCore::MockRealtimeMediaSource::displayDevices):
(WebCore::MockRealtimeMediaSource::MockRealtimeMediaSource):
(WebCore::MockRealtimeMediaSource::initializeCapabilities):
(WebCore::MockRealtimeMediaSource::initializeSettings):
(WebCore::MockRealtimeMediaSource::settings const):
(WebCore::MockRealtimeMediaSource::supportedConstraints):
* platform/mock/MockRealtimeMediaSource.h:
(WebCore::MockRealtimeMediaSource::device const):
* platform/mock/MockRealtimeMediaSourceCenter.cpp:
(WebCore::MockRealtimeMediaSourceCenter::singleton):
(WebCore::MockRealtimeMediaSourceCenter::setMockRealtimeMediaSourceCenterEnabled):
(WebCore::MockRealtimeMediaSourceCenter::setDevices):
(WebCore::MockRealtimeMediaSourceCenter::addDevice):
(WebCore::MockRealtimeMediaSourceCenter::removeDevice):
* platform/mock/MockRealtimeMediaSourceCenter.h:
* platform/mock/MockRealtimeVideoSource.cpp:
(WebCore::MockRealtimeVideoSource::MockRealtimeVideoSource):
(WebCore::MockRealtimeVideoSource::initializeCapabilities):
(WebCore::MockRealtimeVideoSource::drawText):
(WebCore::MockRealtimeVideoSource::generateFrame):
* platform/mock/MockRealtimeVideoSource.h:
(WebCore::MockRealtimeVideoSource::mockCamera const):
(WebCore::MockRealtimeVideoSource::mockScreen const):

Source/WebKit:

Add API to clear, set, remove and reset mock media devices.
The mock media center of UIProcess and all WebProcesses are updated.

* CMakeLists.txt:
* UIProcess/API/C/WKMockMediaDevice.cpp: Added.
(typeFromString):
(WKAddMockMediaDevice):
(WKClearMockMediaDevices):
(WKRemoveMockMediaDevice):
(WKResetMockMediaDevices):
* UIProcess/API/C/WKMockMediaDevice.h: Added.
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::addMockMediaDevice):
(WebKit::WebProcessPool::clearMockMediaDevices):
(WebKit::WebProcessPool::removeMockMediaDevice):
(WebKit::WebProcessPool::resetMockMediaDevices):
* UIProcess/WebProcessPool.h:
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::addMockMediaDevice):
(WebKit::WebProcess::clearMockMediaDevices):
(WebKit::WebProcess::removeMockMediaDevice):
(WebKit::WebProcess::resetMockMediaDevices):
* WebProcess/WebProcess.h:
* WebProcess/WebProcess.messages.in:

Tools:

Add test runner API to clear/add/remove/reset mock media devices.

* WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
* WebKitTestRunner/InjectedBundle/TestRunner.cpp:
(WTR::TestRunner::addMockMediaDevice):
(WTR::TestRunner::addMockCameraDevice):
(WTR::TestRunner::addMockMicrophoneDevice):
(WTR::TestRunner::addMockScreenDevice):
(WTR::TestRunner::clearMockMediaDevices):
(WTR::TestRunner::removeMockMediaDevice):
(WTR::TestRunner::resetMockMediaDevices):
* WebKitTestRunner/InjectedBundle/TestRunner.h:
* WebKitTestRunner/TestController.cpp:
(WTR::TestController::addMockMediaDevice):
(WTR::TestController::clearMockMediaDevices):
(WTR::TestController::removeMockMediaDevice):
(WTR::TestController::resetMockMediaDevices):
* WebKitTestRunner/TestController.h:
* WebKitTestRunner/TestInvocation.cpp:
(WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):

LayoutTests:

* fast/mediastream/device-change-event-2-expected.txt: Added.
* fast/mediastream/device-change-event-2.html: Added.

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

34 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/mediastream/device-change-event-2-expected.txt [new file with mode: 0644]
LayoutTests/fast/mediastream/device-change-event-2.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/mediastream/MediaDevices.cpp
Source/WebCore/Modules/mediastream/MediaDevices.h
Source/WebCore/Modules/mediastream/MediaDevices.idl
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/platform/mediastream/RealtimeMediaSourceCenter.h
Source/WebCore/platform/mock/MockMediaDevice.h [new file with mode: 0644]
Source/WebCore/platform/mock/MockRealtimeAudioSource.cpp
Source/WebCore/platform/mock/MockRealtimeMediaSource.cpp
Source/WebCore/platform/mock/MockRealtimeMediaSource.h
Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp
Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h
Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp
Source/WebCore/platform/mock/MockRealtimeVideoSource.h
Source/WebKit/CMakeLists.txt
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/API/C/WKMockMediaDevice.cpp [new file with mode: 0644]
Source/WebKit/UIProcess/API/C/WKMockMediaDevice.h [new file with mode: 0644]
Source/WebKit/UIProcess/WebProcessPool.cpp
Source/WebKit/UIProcess/WebProcessPool.h
Source/WebKit/WebKit.xcodeproj/project.pbxproj
Source/WebKit/WebProcess/WebProcess.cpp
Source/WebKit/WebProcess/WebProcess.h
Source/WebKit/WebProcess/WebProcess.messages.in
Tools/ChangeLog
Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl
Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp
Tools/WebKitTestRunner/InjectedBundle/TestRunner.h
Tools/WebKitTestRunner/TestController.cpp
Tools/WebKitTestRunner/TestController.h
Tools/WebKitTestRunner/TestInvocation.cpp

index 9d67f5c..b72e342 100644 (file)
@@ -1,3 +1,13 @@
+2018-06-25  Youenn Fablet  <youenn@apple.com>
+
+        Add API to control mock media devices
+        https://bugs.webkit.org/show_bug.cgi?id=186958
+
+        Reviewed by Eric Carlson.
+
+        * fast/mediastream/device-change-event-2-expected.txt: Added.
+        * fast/mediastream/device-change-event-2.html: Added.
+
 2018-06-25  Antoine Quint  <graouts@apple.com>
 
         [Web Animations] Make imported/mozilla/css-animations/test_animation-pausing.html pass reliably
diff --git a/LayoutTests/fast/mediastream/device-change-event-2-expected.txt b/LayoutTests/fast/mediastream/device-change-event-2-expected.txt
new file mode 100644 (file)
index 0000000..64c6b61
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS 'devicechange' event fired when device list changes 
+
diff --git a/LayoutTests/fast/mediastream/device-change-event-2.html b/LayoutTests/fast/mediastream/device-change-event-2.html
new file mode 100644 (file)
index 0000000..43d5f2b
--- /dev/null
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <title>Testing local audio capture playback causes "playing" event to fire</title>
+    <script src="../../resources/testharness.js"></script>
+    <script src="../../resources/testharnessreport.js"></script>
+    <script>
+    let deviceIds = [];
+    promise_test(async (test) => {
+        if (!window.testRunner)
+            return Promise.reject("test requires internal API");
+
+        test.add_cleanup(() => { testRunner.resetMockMediaDevices(); });
+
+        testRunner.setUserMediaPermission(true);
+
+        await navigator.mediaDevices.getUserMedia({ audio:true, video:true });
+
+        let devices = await navigator.mediaDevices.enumerateDevices();
+
+        assert_true(!!devices.length, "check there are some devices");
+
+        testRunner.clearMockMediaDevices();
+
+        devices = await navigator.mediaDevices.enumerateDevices();
+        assert_false(!!devices.length, "check there are no more devices");
+
+        await new Promise((resolve, reject) => {
+            navigator.mediaDevices.ondevicechange = resolve;
+            setTimeout(() => {
+                console.log("event 1 taking a long time");
+                resolve();
+            }, 5000);
+            testRunner.addMockCameraDevice("id1", "my camera");
+        });
+        devices = await navigator.mediaDevices.enumerateDevices();
+        assert_equals(devices[0].kind, "videoinput");
+        assert_equals(devices[0].label, "my camera");
+
+        await new Promise((resolve, reject) => {
+            navigator.mediaDevices.ondevicechange = resolve;
+            setTimeout(() => {
+                console.log("event 2 taking a long time");
+                resolve();
+            }, 5000);
+            testRunner.addMockMicrophoneDevice("id2", "my mic");
+        });
+        devices = await navigator.mediaDevices.enumerateDevices();
+        assert_equals(devices[0].kind, "audioinput");
+        assert_equals(devices[0].label, "my mic");
+
+    }, "'devicechange' event fired when device list changes");
+    </script>
+</head>
+<body>
+</body>
+</html>
index cc1d334..fb9eeaf 100644 (file)
@@ -1,3 +1,82 @@
+2018-06-25  Youenn Fablet  <youenn@apple.com>
+
+        Add API to control mock media devices
+        https://bugs.webkit.org/show_bug.cgi?id=186958
+
+        Reviewed by Eric Carlson.
+
+        Refactor code to introduce a MockDevice structure that can be used in multiple processes.
+        Update mock sources and center to use MockDevice.
+        Add API to update mock media devices.
+
+        Make MediaDevices an ActiveDOMObject so that it does not get collected when ondevicechange is set.
+
+         Test: fast/mediastream/device-change-event-2.html
+
+        * Modules/mediastream/MediaDevices.cpp:
+        (WebCore::MediaDevices::MediaDevices):
+        (WebCore::MediaDevices::stop):
+        (WebCore::MediaDevices::scheduledEventTimerFired):
+        (WebCore::MediaDevices::hasPendingActivity const):
+        (WebCore::MediaDevices::activeDOMObjectName const):
+        (WebCore::MediaDevices::canSuspendForDocumentSuspension const):
+        * Modules/mediastream/MediaDevices.h:
+        * Modules/mediastream/MediaDevices.idl:
+        * WebCore.xcodeproj/project.pbxproj:
+        * platform/mediastream/RealtimeMediaSourceCenter.h:
+        * platform/mock/MockMediaDevice.h: Added.
+        (WebCore::MockMicrophoneProperties::encode const):
+        (WebCore::MockMicrophoneProperties::decode):
+        (WebCore::MockCameraProperties::encode const):
+        (WebCore::MockCameraProperties::decode):
+        (WebCore::MockDisplayProperties::encode const):
+        (WebCore::MockDisplayProperties::decode):
+        (WebCore::MockMediaDevice::isMicrophone const):
+        (WebCore::MockMediaDevice::isCamera const):
+        (WebCore::MockMediaDevice::isDisplay const):
+        (WebCore::MockMediaDevice::type const):
+        (WebCore::MockMediaDevice::encode const):
+        (WebCore::MockMediaDevice::decodeMockMediaDevice):
+        (WebCore::MockMediaDevice::decode):
+        * platform/mock/MockRealtimeAudioSource.cpp:
+        (WebCore::MockRealtimeAudioSource::startProducingData):
+        * platform/mock/MockRealtimeMediaSource.cpp:
+        (WebCore::defaultDevices):
+        (WebCore::devices):
+        (WebCore::deviceMap):
+        (WebCore::deviceListForDevice):
+        (WebCore::MockRealtimeMediaSource::createCaptureDevice):
+        (WebCore::MockRealtimeMediaSource::resetDevices):
+        (WebCore::MockRealtimeMediaSource::setDevices):
+        (WebCore::MockRealtimeMediaSource::addDevice):
+        (WebCore::MockRealtimeMediaSource::removeDevice):
+        (WebCore::MockRealtimeMediaSource::captureDeviceWithPersistentID):
+        (WebCore::MockRealtimeMediaSource::audioDevices):
+        (WebCore::MockRealtimeMediaSource::videoDevices):
+        (WebCore::MockRealtimeMediaSource::displayDevices):
+        (WebCore::MockRealtimeMediaSource::MockRealtimeMediaSource):
+        (WebCore::MockRealtimeMediaSource::initializeCapabilities):
+        (WebCore::MockRealtimeMediaSource::initializeSettings):
+        (WebCore::MockRealtimeMediaSource::settings const):
+        (WebCore::MockRealtimeMediaSource::supportedConstraints):
+        * platform/mock/MockRealtimeMediaSource.h:
+        (WebCore::MockRealtimeMediaSource::device const):
+        * platform/mock/MockRealtimeMediaSourceCenter.cpp:
+        (WebCore::MockRealtimeMediaSourceCenter::singleton):
+        (WebCore::MockRealtimeMediaSourceCenter::setMockRealtimeMediaSourceCenterEnabled):
+        (WebCore::MockRealtimeMediaSourceCenter::setDevices):
+        (WebCore::MockRealtimeMediaSourceCenter::addDevice):
+        (WebCore::MockRealtimeMediaSourceCenter::removeDevice):
+        * platform/mock/MockRealtimeMediaSourceCenter.h:
+        * platform/mock/MockRealtimeVideoSource.cpp:
+        (WebCore::MockRealtimeVideoSource::MockRealtimeVideoSource):
+        (WebCore::MockRealtimeVideoSource::initializeCapabilities):
+        (WebCore::MockRealtimeVideoSource::drawText):
+        (WebCore::MockRealtimeVideoSource::generateFrame):
+        * platform/mock/MockRealtimeVideoSource.h:
+        (WebCore::MockRealtimeVideoSource::mockCamera const):
+        (WebCore::MockRealtimeVideoSource::mockScreen const):
+
 2018-06-25  Simon Fraser  <simon.fraser@apple.com>
 
         AutoTableLayout wastes 52KB of Vector capacity on nytimes.com
index af3bf13..be9051c 100644 (file)
@@ -47,8 +47,9 @@
 namespace WebCore {
 
 inline MediaDevices::MediaDevices(Document& document)
-    : ContextDestructionObserver(&document)
+    : ActiveDOMObject(&document)
     , m_scheduledEventTimer(*this, &MediaDevices::scheduledEventTimerFired)
+    , m_eventNames(eventNames())
 {
     m_deviceChangedToken = RealtimeMediaSourceCenter::singleton().addDevicesChangedObserver([weakThis = makeWeakPtr(*this), this]() {
 
@@ -61,16 +62,20 @@ inline MediaDevices::MediaDevices(Document& document)
             m_scheduledEventTimer.startOneShot(Seconds(randomNumber() / 2));
     });
 
+    suspendIfNeeded();
+
     static_assert(static_cast<size_t>(MediaDevices::DisplayCaptureSurfaceType::Monitor) == static_cast<size_t>(RealtimeMediaSourceSettings::DisplaySurfaceType::Monitor), "MediaDevices::DisplayCaptureSurfaceType::Monitor is not equal to RealtimeMediaSourceSettings::DisplaySurfaceType::Monitor as expected");
     static_assert(static_cast<size_t>(MediaDevices::DisplayCaptureSurfaceType::Window) == static_cast<size_t>(RealtimeMediaSourceSettings::DisplaySurfaceType::Window), "MediaDevices::DisplayCaptureSurfaceType::Window is not RealtimeMediaSourceSettings::DisplaySurfaceType::Window as expected");
     static_assert(static_cast<size_t>(MediaDevices::DisplayCaptureSurfaceType::Application) == static_cast<size_t>(RealtimeMediaSourceSettings::DisplaySurfaceType::Application), "MediaDevices::DisplayCaptureSurfaceType::Application is not RealtimeMediaSourceSettings::DisplaySurfaceType::Application as expected");
     static_assert(static_cast<size_t>(MediaDevices::DisplayCaptureSurfaceType::Browser) == static_cast<size_t>(RealtimeMediaSourceSettings::DisplaySurfaceType::Browser), "MediaDevices::DisplayCaptureSurfaceType::Browser is not RealtimeMediaSourceSettings::DisplaySurfaceType::Browser as expected");
 }
 
-MediaDevices::~MediaDevices()
+MediaDevices::~MediaDevices() = default;
+
+void MediaDevices::stop()
 {
     if (m_deviceChangedToken)
-        RealtimeMediaSourceCenter::singleton().removeDevicesChangedObserver(m_deviceChangedToken.value());
+        RealtimeMediaSourceCenter::singleton().removeDevicesChangedObserver(m_deviceChangedToken);
 }
 
 Ref<MediaDevices> MediaDevices::create(Document& document)
@@ -157,7 +162,23 @@ MediaTrackSupportedConstraints MediaDevices::getSupportedConstraints()
 
 void MediaDevices::scheduledEventTimerFired()
 {
-    dispatchEvent(Event::create(eventNames().devicechangeEvent, false, false));
+    if (scriptExecutionContext())
+        dispatchEvent(Event::create(eventNames().devicechangeEvent, false, false));
+}
+
+bool MediaDevices::hasPendingActivity() const
+{
+    return scriptExecutionContext() && hasEventListeners(m_eventNames.devicechangeEvent);
+}
+
+const char* MediaDevices::activeDOMObjectName() const
+{
+    return "MediaDevices";
+}
+
+bool MediaDevices::canSuspendForDocumentSuspension() const
+{
+    return true;
 }
 
 } // namespace WebCore
index ae35d5e..02d1aef 100644 (file)
@@ -33,6 +33,8 @@
 
 #if ENABLE(MEDIA_STREAM)
 
+#include "ActiveDOMObject.h"
+#include "EventNames.h"
 #include "EventTarget.h"
 #include "ExceptionOr.h"
 #include "JSDOMPromiseDeferred.h"
@@ -49,7 +51,7 @@ class MediaStream;
 
 struct MediaTrackSupportedConstraints;
 
-class MediaDevices : public RefCounted<MediaDevices>, public ContextDestructionObserver, public EventTargetWithInlineData, public CanMakeWeakPtr<MediaDevices> {
+class MediaDevices : public RefCounted<MediaDevices>, public ActiveDOMObject, public EventTargetWithInlineData, public CanMakeWeakPtr<MediaDevices> {
 public:
     static Ref<MediaDevices> create(Document&);
 
@@ -84,14 +86,23 @@ private:
 
     void scheduledEventTimerFired();
 
+    friend class JSMediaDevicesOwner;
+
+    // ActiveDOMObject
+    const char* activeDOMObjectName() const final;
+    bool canSuspendForDocumentSuspension() const final;
+    void stop() final;
+    bool hasPendingActivity() const final;
+
     // EventTargetWithInlineData.
-    EventTargetInterface eventTargetInterface() const override { return MediaDevicesEventTargetInterfaceType; }
-    ScriptExecutionContext* scriptExecutionContext() const final { return m_scriptExecutionContext; }
-    void refEventTarget() override { ref(); }
-    void derefEventTarget() override { deref(); }
+    EventTargetInterface eventTargetInterface() const final { return MediaDevicesEventTargetInterfaceType; }
+    ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); }
+    void refEventTarget() final { ref(); }
+    void derefEventTarget() final { deref(); }
 
     Timer m_scheduledEventTimer;
-    std::optional<RealtimeMediaSourceCenter::DevicesChangedObserverToken> m_deviceChangedToken;
+    RealtimeMediaSourceCenter::DevicesChangedObserverToken m_deviceChangedToken;
+    const EventNames& m_eventNames; // Need to cache this so we can use it from GC threads.
 };
 
 } // namespace WebCore
index 31a04de..d7ba3ed 100644 (file)
@@ -30,6 +30,7 @@
 
 // FIXME 169871: should be subclass of EventTarget
 [
+    ActiveDOMObject,
     Conditional=MEDIA_STREAM,
     NoInterfaceObject,
 ] interface MediaDevices {
index 206c495..f6836a6 100644 (file)
                4138D3351244054800323D33 /* EventContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 4138D3331244054800323D33 /* EventContext.h */; };
                4138F8581D253F0E001CB61E /* JSDOMIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 4138F8561D253EEE001CB61E /* JSDOMIterator.h */; settings = {ATTRIBUTES = (Private, ); }; };
                413C2C341BC29A8F0075204C /* JSDOMConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = 413C2C331BC29A7B0075204C /* JSDOMConstructor.h */; };
+               413CCD4A20DE034F0065A21A /* MockMediaDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 413CCD4820DE013C0065A21A /* MockMediaDevice.h */; settings = {ATTRIBUTES = (Private, ); }; };
                413E00791DB0E4F2002341D2 /* MemoryRelease.h in Headers */ = {isa = PBXBuildFile; fileRef = 413E00781DB0E4DE002341D2 /* MemoryRelease.h */; settings = {ATTRIBUTES = (Private, ); }; };
                414B82051D6DF0E50077EBE3 /* StructuredClone.h in Headers */ = {isa = PBXBuildFile; fileRef = 414B82031D6DF0D90077EBE3 /* StructuredClone.h */; };
                414DEDE71F9FE91E0047C40D /* EmptyFrameLoaderClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 414DEDE51F9FE9150047C40D /* EmptyFrameLoaderClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4138F8551D253EEE001CB61E /* JSDOMIterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDOMIterator.cpp; sourceTree = "<group>"; };
                4138F8561D253EEE001CB61E /* JSDOMIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDOMIterator.h; sourceTree = "<group>"; };
                413C2C331BC29A7B0075204C /* JSDOMConstructor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDOMConstructor.h; sourceTree = "<group>"; };
+               413CCD4820DE013C0065A21A /* MockMediaDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MockMediaDevice.h; sourceTree = "<group>"; };
                413E00771DB0E4DE002341D2 /* MemoryRelease.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryRelease.cpp; sourceTree = "<group>"; };
                413E00781DB0E4DE002341D2 /* MemoryRelease.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryRelease.h; sourceTree = "<group>"; };
                413E007B1DB0E707002341D2 /* MemoryReleaseCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MemoryReleaseCocoa.mm; sourceTree = "<group>"; };
                                077B64151B95F703003E9AD5 /* MediaPlaybackTargetMock.h */,
                                077B64101B94F12E003E9AD5 /* MediaPlaybackTargetPickerMock.cpp */,
                                077B64111B94F12E003E9AD5 /* MediaPlaybackTargetPickerMock.h */,
+                               413CCD4820DE013C0065A21A /* MockMediaDevice.h */,
                                07D6A4F11BED5F8800174146 /* MockRealtimeAudioSource.cpp */,
                                07D6A4F21BED5F8800174146 /* MockRealtimeAudioSource.h */,
                                07D6A4ED1BECF2D200174146 /* MockRealtimeMediaSource.cpp */,
                                CDF4B7201E03BF6F00E235A2 /* MockCDMFactory.idl in Headers */,
                                51058AE01D67C229009A538C /* MockGamepad.h in Headers */,
                                51058AE21D67C229009A538C /* MockGamepadProvider.h in Headers */,
+                               413CCD4A20DE034F0065A21A /* MockMediaDevice.h in Headers */,
                                CDF2B0131820540600F2B424 /* MockMediaPlayerMediaSource.h in Headers */,
                                CDF2B0151820540600F2B424 /* MockMediaSourcePrivate.h in Headers */,
                                07D6A4F41BED5F8800174146 /* MockRealtimeAudioSource.h in Headers */,
index 0c725f9..61ff0ea 100644 (file)
@@ -88,13 +88,14 @@ public:
     using DevicesChangedObserverToken = unsigned;
     DevicesChangedObserverToken addDevicesChangedObserver(std::function<void()>&&);
     void removeDevicesChangedObserver(DevicesChangedObserverToken);
-    void captureDevicesChanged();
 
     void setVideoCapturePageState(bool, bool);
 
 protected:
     RealtimeMediaSourceCenter();
 
+    void captureDevicesChanged();
+
     static RealtimeMediaSourceCenter& platformCenter();
     RealtimeMediaSourceSupportedConstraints m_supportedConstraints;
 
diff --git a/Source/WebCore/platform/mock/MockMediaDevice.h b/Source/WebCore/platform/mock/MockMediaDevice.h
new file mode 100644 (file)
index 0000000..882dbaf
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(MEDIA_STREAM)
+
+#include "CaptureDevice.h"
+#include "RealtimeMediaSource.h"
+
+namespace WebCore {
+
+struct MockMicrophoneProperties {
+    template<class Encoder>
+    void encode(Encoder& encoder) const
+    {
+        encoder << static_cast<int32_t>(defaultSampleRate);
+    }
+
+    template <class Decoder>
+    static std::optional<MockMicrophoneProperties> decode(Decoder& decoder)
+    {
+        std::optional<int32_t> defaultSampleRate;
+        decoder >> defaultSampleRate;
+        if (!defaultSampleRate)
+            return std::nullopt;
+        return MockMicrophoneProperties { *defaultSampleRate };
+    }
+
+    int defaultSampleRate { 44100 };
+};
+
+// FIXME: Add support for other properties and serialization of colors.
+struct MockCameraProperties {
+    template<class Encoder>
+    void encode(Encoder& encoder) const
+    {
+        encoder << defaultFrameRate;
+        encoder << facingModeCapability;
+    }
+
+    template <class Decoder>
+    static std::optional<MockCameraProperties> decode(Decoder& decoder)
+    {
+        std::optional<double> defaultFrameRate;
+        decoder >> defaultFrameRate;
+        if (!defaultFrameRate)
+            return std::nullopt;
+
+        std::optional<RealtimeMediaSourceSettings::VideoFacingMode> facingModeCapability;
+        decoder >> facingModeCapability;
+        if (!facingModeCapability)
+            return std::nullopt;
+
+        return MockCameraProperties { *defaultFrameRate, *facingModeCapability, Color::black };
+    }
+
+    double defaultFrameRate { 30 };
+    RealtimeMediaSourceSettings::VideoFacingMode facingModeCapability { RealtimeMediaSourceSettings::VideoFacingMode::User };
+    Color fillColor { Color::black };
+};
+
+struct MockDisplayProperties {
+    template<class Encoder>
+    void encode(Encoder& encoder) const
+    {
+        encoder << defaultFrameRate;
+    }
+
+    template <class Decoder>
+    static std::optional<MockDisplayProperties> decode(Decoder& decoder)
+    {
+        std::optional<double> defaultFrameRate;
+        decoder >> defaultFrameRate;
+        if (!defaultFrameRate)
+            return std::nullopt;
+
+        return MockDisplayProperties { *defaultFrameRate, Color::lightGray };
+    }
+
+    double defaultFrameRate { 30 };
+    Color fillColor { Color::lightGray };
+};
+
+struct MockMediaDevice {
+    bool isMicrophone() const { return WTF::holds_alternative<MockMicrophoneProperties>(properties); }
+    bool isCamera() const { return WTF::holds_alternative<MockCameraProperties>(properties); }
+    bool isDisplay() const { return WTF::holds_alternative<MockDisplayProperties>(properties); }
+
+    CaptureDevice::DeviceType type() const
+    {
+        if (isMicrophone())
+            return CaptureDevice::DeviceType::Microphone;
+        if (isCamera())
+            return CaptureDevice::DeviceType::Camera;
+        ASSERT(isDisplay());
+        return CaptureDevice::DeviceType::Screen;
+    }
+
+    template<class Encoder>
+    void encode(Encoder& encoder) const
+    {
+        encoder << persistentId;
+        encoder << label;
+        switchOn(properties, [&](const MockMicrophoneProperties& properties) {
+            encoder << (uint8_t)1;
+            encoder << properties;
+        }, [&](const MockCameraProperties& properties) {
+            encoder << (uint8_t)2;
+            encoder << properties;
+        }, [&](const MockDisplayProperties& properties) {
+            encoder << (uint8_t)3;
+            encoder << properties;
+        });
+    }
+
+    template <typename Properties, typename Decoder>
+    static std::optional<MockMediaDevice> decodeMockMediaDevice(Decoder& decoder, String&& persistentId, String&& label)
+    {
+        std::optional<Properties> properties;
+        decoder >> properties;
+        if (!properties)
+            return std::nullopt;
+        return MockMediaDevice { WTFMove(persistentId), WTFMove(label), WTFMove(*properties) };
+    }
+
+    template <class Decoder>
+    static std::optional<MockMediaDevice> decode(Decoder& decoder)
+    {
+        std::optional<String> persistentId;
+        decoder >> persistentId;
+        if (!persistentId)
+            return std::nullopt;
+
+        std::optional<String> label;
+        decoder >> label;
+        if (!label)
+            return std::nullopt;
+
+        std::optional<uint8_t> index;
+        decoder >> index;
+        if (!index)
+            return std::nullopt;
+
+        switch (*index) {
+        case 1:
+            return decodeMockMediaDevice<MockMicrophoneProperties>(decoder, WTFMove(*persistentId), WTFMove(*label));
+        case 2:
+            return decodeMockMediaDevice<MockCameraProperties>(decoder, WTFMove(*persistentId), WTFMove(*label));
+        case 3:
+            return decodeMockMediaDevice<MockDisplayProperties>(decoder, WTFMove(*persistentId), WTFMove(*label));
+        }
+        return std::nullopt;
+    }
+
+    String persistentId;
+    String label;
+    Variant<MockMicrophoneProperties, MockCameraProperties, MockDisplayProperties> properties;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM)
index 9c6024e..002caf3 100644 (file)
@@ -118,7 +118,7 @@ void MockRealtimeAudioSource::startProducingData()
 #endif
 
     if (!sampleRate())
-        setSampleRate(device() == MockRealtimeMediaSource::MockDevice::Microphone1 ? 44100 : 48000);
+        setSampleRate(WTF::get<MockMicrophoneProperties>(device().properties).defaultSampleRate);
 
     m_startTime = MonotonicTime::now();
     m_timer.startRepeating(renderInterval());
index 4644f08..0875497 100644 (file)
 
 namespace WebCore {
 
-struct MockDeviceInfo {
-    ASCIILiteral id { ASCIILiteral::null() };
-    CaptureDevice::DeviceType type;
-    ASCIILiteral name { ASCIILiteral::null() };
-    MockRealtimeMediaSource::MockDevice device;
-};
-
-static const HashMap<String, MockDeviceInfo>& deviceMap()
-{
-    static const auto infoMap = makeNeverDestroyed([] {
-        static const MockDeviceInfo devices[] = {
-            { "239c24b0-2b15-11e3-8224-0800200c9a66"_s, CaptureDevice::DeviceType::Microphone, "Mock audio device 1"_s, MockRealtimeMediaSource::MockDevice::Microphone1 },
-            { "239c24b1-2b15-11e3-8224-0800200c9a66"_s, CaptureDevice::DeviceType::Microphone, "Mock audio device 2"_s, MockRealtimeMediaSource::MockDevice::Microphone2 },
-
-            { "239c24b2-2b15-11e3-8224-0800200c9a66"_s, CaptureDevice::DeviceType::Camera, "Mock video device 1"_s, MockRealtimeMediaSource::MockDevice::Camera1 },
-            { "239c24b3-2b15-11e3-8224-0800200c9a66"_s, CaptureDevice::DeviceType::Camera, "Mock video device 2"_s, MockRealtimeMediaSource::MockDevice::Camera2 },
-
-            { "SCREEN-1"_s, CaptureDevice::DeviceType::Screen, "Mock screen device 1"_s, MockRealtimeMediaSource::MockDevice::Screen1 },
-            { "SCREEN-2"_s, CaptureDevice::DeviceType::Screen, "Mock screen device 2"_s, MockRealtimeMediaSource::MockDevice::Screen2 },
-        };
-
-        HashMap<String, MockDeviceInfo> map;
-        for (auto& info : devices)
-            map.add(info.id, info);
+static inline Vector<MockMediaDevice> defaultDevices()
+{
+    return Vector<MockMediaDevice> {
+        MockMediaDevice { "239c24b0-2b15-11e3-8224-0800200c9a66"_s, "Mock audio device 1"_s, MockMicrophoneProperties { 44100 } },
+        MockMediaDevice { "239c24b1-2b15-11e3-8224-0800200c9a66"_s, "Mock audio device 2"_s, MockMicrophoneProperties { 48000 } },
+
+        MockMediaDevice { "239c24b2-2b15-11e3-8224-0800200c9a66"_s, "Mock video device 1"_s, MockCameraProperties { 30, RealtimeMediaSourceSettings::VideoFacingMode::User, Color::black } },
+        MockMediaDevice { "239c24b3-2b15-11e3-8224-0800200c9a66"_s, "Mock video device 2"_s, MockCameraProperties { 15, RealtimeMediaSourceSettings::VideoFacingMode::Environment, Color::darkGray } },
+
+        MockMediaDevice { "SCREEN-1"_s, "Mock screen device 1"_s, MockDisplayProperties { 30, Color::lightGray } },
+        MockMediaDevice { "SCREEN-2"_s, "Mock screen device 2"_s, MockDisplayProperties { 10, Color::yellow } }
+    };
+}
+
+static Vector<MockMediaDevice>& devices()
+{
+    static auto devices = makeNeverDestroyed([] {
+        return defaultDevices();
+    }());
+    return devices;
+}
+
+static HashMap<String, MockMediaDevice>& deviceMap()
+{
+    static auto map = makeNeverDestroyed([] {
+        HashMap<String, MockMediaDevice> map;
+        for (auto& device : devices())
+            map.add(device.persistentId, device);
+
         return map;
     }());
+    return map;
+}
+
+static inline Vector<CaptureDevice>& deviceListForDevice(const MockMediaDevice& device)
+{
+    if (device.isMicrophone())
+        return MockRealtimeAudioSource::audioDevices();
+    if (device.isCamera())
+        return MockRealtimeAudioSource::videoDevices();
 
-    return infoMap;
+    ASSERT(device.isDisplay());
+    return MockRealtimeAudioSource::displayDevices();
 }
 
-std::optional<CaptureDevice> MockRealtimeMediaSource::captureDeviceWithPersistentID(CaptureDevice::DeviceType type, const String& id)
+void MockRealtimeMediaSource::createCaptureDevice(const MockMediaDevice& device)
 {
-    ASSERT(!id.isEmpty());
+    deviceListForDevice(device).append(captureDeviceWithPersistentID(device.type(), device.persistentId).value());
+}
+
+void MockRealtimeMediaSource::resetDevices()
+{
+    setDevices(defaultDevices());
+}
+
+void MockRealtimeMediaSource::setDevices(Vector<MockMediaDevice>&& newMockDevices)
+{
+    audioDevices().clear();
+    videoDevices().clear();
+    displayDevices().clear();
 
-    auto map = deviceMap();
-    auto it = map.find(id);
-    if (it != map.end() && it->value.type == type) {
-        auto device = CaptureDevice(it->value.id, it->value.type, it->value.name);
-        device.setEnabled(true);
-        return WTFMove(device);
+    auto& mockDevices = devices();
+    mockDevices = WTFMove(newMockDevices);
+
+    auto& map = deviceMap();
+    map.clear();
+
+    for (const auto& device : mockDevices) {
+        map.add(device.persistentId, device);
+        createCaptureDevice(device);
     }
+}
 
-    return std::nullopt;
+void MockRealtimeMediaSource::addDevice(const MockMediaDevice& device)
+{
+    devices().append(device);
+    deviceMap().set(device.persistentId, device);
+    createCaptureDevice(device);
 }
 
-Vector<CaptureDevice>& MockRealtimeMediaSource::audioDevices()
+void MockRealtimeMediaSource::removeDevice(const String& persistentId)
 {
-    static auto info = makeNeverDestroyed([] {
-        Vector<CaptureDevice> vector;
+    auto& map = deviceMap();
+    auto iterator = map.find(persistentId);
+    if (iterator == map.end())
+        return;
 
-        auto captureDevice = captureDeviceWithPersistentID(CaptureDevice::DeviceType::Microphone, "239c24b0-2b15-11e3-8224-0800200c9a66"_s);
-        ASSERT(captureDevice);
-        vector.append(WTFMove(captureDevice.value()));
+    devices().removeFirstMatching([&persistentId](const auto& device) {
+        return device.persistentId == persistentId;
+    });
 
-        captureDevice = captureDeviceWithPersistentID(CaptureDevice::DeviceType::Microphone, "239c24b1-2b15-11e3-8224-0800200c9a66"_s);
-        ASSERT(captureDevice);
-        vector.append(WTFMove(captureDevice.value()));
+    deviceListForDevice(iterator->value).removeFirstMatching([&persistentId](const auto& device) {
+        return device.persistentId() == persistentId;
+    });
 
-        return vector;
-    }());
-    return info;
+    map.remove(iterator);
 }
 
-Vector<CaptureDevice>& MockRealtimeMediaSource::videoDevices()
+std::optional<CaptureDevice> MockRealtimeMediaSource::captureDeviceWithPersistentID(CaptureDevice::DeviceType type, const String& id)
 {
-    static auto info = makeNeverDestroyed([] {
-        Vector<CaptureDevice> vector;
+    ASSERT(!id.isEmpty());
 
-        auto captureDevice = captureDeviceWithPersistentID(CaptureDevice::DeviceType::Camera, "239c24b2-2b15-11e3-8224-0800200c9a66"_s);
-        ASSERT(captureDevice);
-        vector.append(WTFMove(captureDevice.value()));
+    auto& map = deviceMap();
+    auto iterator = map.find(id);
+    if (iterator == map.end() || iterator->value.type() != type)
+        return std::nullopt;
 
-        captureDevice = captureDeviceWithPersistentID(CaptureDevice::DeviceType::Camera, "239c24b3-2b15-11e3-8224-0800200c9a66"_s);
-        ASSERT(captureDevice);
-        vector.append(WTFMove(captureDevice.value()));
+    CaptureDevice device { iterator->value.persistentId, type, iterator->value.label };
+    device.setEnabled(true);
+    return WTFMove(device);
+}
+
+Vector<CaptureDevice>& MockRealtimeMediaSource::audioDevices()
+{
+    static auto audioDevices = makeNeverDestroyed([] {
+        Vector<CaptureDevice> audioDevices;
+        for (const auto& device : devices()) {
+            if (device.type() == CaptureDevice::DeviceType::Microphone)
+                audioDevices.append(captureDeviceWithPersistentID(CaptureDevice::DeviceType::Microphone, device.persistentId).value());
+        }
+        return audioDevices;
+    }());
+    return audioDevices;
+}
 
-        return vector;
+Vector<CaptureDevice>& MockRealtimeMediaSource::videoDevices()
+{
+    static auto videoDevices = makeNeverDestroyed([] {
+        Vector<CaptureDevice> videoDevices;
+        for (const auto& device : devices()) {
+            if (device.type() == CaptureDevice::DeviceType::Camera)
+                videoDevices.append(captureDeviceWithPersistentID(CaptureDevice::DeviceType::Camera, device.persistentId).value());
+        }
+        return videoDevices;
     }());
-    return info;
+    return videoDevices;
 }
 
 Vector<CaptureDevice>& MockRealtimeMediaSource::displayDevices()
 {
-    static auto devices = makeNeverDestroyed([] {
-        Vector<CaptureDevice> vector;
-
-        auto captureDevice = captureDeviceWithPersistentID(CaptureDevice::DeviceType::Screen, "SCREEN-1"_s);
-        ASSERT(captureDevice);
-        vector.append(WTFMove(captureDevice.value()));
-
-        captureDevice = captureDeviceWithPersistentID(CaptureDevice::DeviceType::Screen, "SCREEN-2"_s);
-        ASSERT(captureDevice);
-        vector.append(WTFMove(captureDevice.value()));
-
-        return vector;
+    static auto displayDevices = makeNeverDestroyed([] {
+        Vector<CaptureDevice> displayDevices;
+        for (const auto& device : devices()) {
+            if (device.type() == CaptureDevice::DeviceType::Screen)
+                displayDevices.append(captureDeviceWithPersistentID(CaptureDevice::DeviceType::Screen, device.persistentId).value());
+        }
+        return displayDevices;
     }());
 
-    return devices;
+    return displayDevices;
 }
 
 MockRealtimeMediaSource::MockRealtimeMediaSource(const String& id, RealtimeMediaSource::Type type, const String& name)
     : RealtimeMediaSource(id, type, name)
+    , m_device(deviceMap().get(id))
 {
     ASSERT(type != RealtimeMediaSource::Type::None);
-
-    auto map = deviceMap();
-    auto it = map.find(id);
-    ASSERT(it != map.end());
-
-    m_device = it->value.device;
     setPersistentID(String(id));
 }
 
index a1d3e9b..3355da6 100644 (file)
 
 #if ENABLE(MEDIA_STREAM)
 
+#include "CaptureDevice.h"
+#include "MockMediaDevice.h"
 #include "RealtimeMediaSource.h"
 
 namespace WebCore {
 
-class CaptureDevice;
-
 class MockRealtimeMediaSource : public RealtimeMediaSource {
 public:
     virtual ~MockRealtimeMediaSource() = default;
 
+    static void setDevices(Vector<MockMediaDevice>&&);
+    static void addDevice(const MockMediaDevice&);
+    static void removeDevice(const String& persistentId);
+    WEBCORE_EXPORT static void resetDevices();
+
     static Vector<CaptureDevice>& audioDevices();
     static Vector<CaptureDevice>& videoDevices();
     static Vector<CaptureDevice>& displayDevices();
 
     static std::optional<CaptureDevice> captureDeviceWithPersistentID(CaptureDevice::DeviceType, const String&);
 
-    enum class MockDevice { Invalid, Microphone1, Microphone2, Camera1, Camera2, Screen1, Screen2 };
-
 protected:
     MockRealtimeMediaSource(const String& id, Type, const String& name);
 
+    static void createCaptureDevice(const MockMediaDevice&);
+
     virtual void updateSettings(RealtimeMediaSourceSettings&) = 0;
     virtual void initializeCapabilities(RealtimeMediaSourceCapabilities&) = 0;
     virtual void initializeSupportedConstraints(RealtimeMediaSourceSupportedConstraints&) = 0;
@@ -62,8 +67,8 @@ protected:
 
     RealtimeMediaSourceSupportedConstraints& supportedConstraints();
 
-    MockDevice device() const { return m_device; }
-    MockDevice m_device { MockDevice::Invalid };
+    const MockMediaDevice& device() const { return m_device; }
+    MockMediaDevice m_device;
 
 private:
     void initializeCapabilities();
index 77a0f8e..df91ebe 100644 (file)
 
 namespace WebCore {
 
-void MockRealtimeMediaSourceCenter::setMockRealtimeMediaSourceCenterEnabled(bool enabled)
+MockRealtimeMediaSourceCenter& MockRealtimeMediaSourceCenter::singleton()
 {
     static NeverDestroyed<MockRealtimeMediaSourceCenter> center;
+    return center;
+}
+
+void MockRealtimeMediaSourceCenter::setMockRealtimeMediaSourceCenterEnabled(bool enabled)
+{
     static bool active = false;
     if (active != enabled) {
         active = enabled;
-        RealtimeMediaSourceCenter::setSharedStreamCenterOverride(enabled ? &center.get() : nullptr);
+        RealtimeMediaSourceCenter::setSharedStreamCenterOverride(enabled ? &singleton() : nullptr);
     }
 }
 
+void MockRealtimeMediaSourceCenter::setDevices(Vector<MockMediaDevice>&& newMockDevices)
+{
+    MockRealtimeMediaSource::setDevices(WTFMove(newMockDevices));
+    singleton().captureDevicesChanged();
+}
+
+void MockRealtimeMediaSourceCenter::addDevice(const MockMediaDevice& device)
+{
+    MockRealtimeMediaSource::addDevice(device);
+    singleton().captureDevicesChanged();
+}
+
+void MockRealtimeMediaSourceCenter::removeDevice(const String& persistentId)
+{
+    MockRealtimeMediaSource::removeDevice(persistentId);
+    singleton().captureDevicesChanged();
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(MEDIA_STREAM)
index 0f92f9d..be47a0e 100644 (file)
@@ -39,6 +39,10 @@ class MockRealtimeMediaSourceCenter final : public RealtimeMediaSourceCenter {
 public:
     WEBCORE_EXPORT static void setMockRealtimeMediaSourceCenterEnabled(bool);
 
+    WEBCORE_EXPORT static void setDevices(Vector<MockMediaDevice>&&);
+    WEBCORE_EXPORT static void addDevice(const MockMediaDevice&);
+    WEBCORE_EXPORT static void removeDevice(const String& persistentId);
+
     static RealtimeMediaSource::VideoCaptureFactory& videoCaptureSourceFactory() { return MockRealtimeVideoSource::factory(); }
     static RealtimeMediaSource::AudioCaptureFactory& audioCaptureSourceFactory() { return MockRealtimeAudioSource::factory(); }
 
@@ -46,6 +50,8 @@ private:
     MockRealtimeMediaSourceCenter() = default;
     friend NeverDestroyed<MockRealtimeMediaSourceCenter>;
 
+    static MockRealtimeMediaSourceCenter& singleton();
+
     RealtimeMediaSource::AudioCaptureFactory& audioFactory() final { return MockRealtimeAudioSource::factory(); }
     RealtimeMediaSource::VideoCaptureFactory& videoFactory() final { return MockRealtimeVideoSource::factory(); }
 
index 03a7c3e..9c41e1c 100644 (file)
@@ -108,31 +108,18 @@ MockRealtimeVideoSource::MockRealtimeVideoSource(const String& deviceID, const S
     : MockRealtimeMediaSource(deviceID, RealtimeMediaSource::Type::Video, name)
     , m_timer(RunLoop::current(), this, &MockRealtimeVideoSource::generateFrame)
 {
-    switch (device()) {
-    case MockDevice::Camera1:
-        setFrameRate(30);
-        setFacingMode(RealtimeMediaSourceSettings::User);
-        break;
-    case MockDevice::Camera2:
-        setFrameRate(15);
-        setFacingMode(RealtimeMediaSourceSettings::Environment);
-        break;
-    case MockDevice::Screen1:
-        setFrameRate(30);
-        break;
-    case MockDevice::Screen2:
-        setFrameRate(10);
-        break;
-    case MockDevice::Microphone1:
-    case MockDevice::Microphone2:
-    case MockDevice::Invalid:
-        ASSERT_NOT_REACHED();
-        break;
-    }
-
     m_dashWidths.reserveInitialCapacity(2);
     m_dashWidths.uncheckedAppend(6);
     m_dashWidths.uncheckedAppend(6);
+
+    if (mockScreen()) {
+        setFrameRate(WTF::get<MockDisplayProperties>(device().properties).defaultFrameRate);
+        return;
+    }
+
+    auto& properties = WTF::get<MockCameraProperties>(device().properties);
+    setFrameRate(properties.defaultFrameRate);
+    setFacingMode(properties.facingModeCapability);
 }
 
 MockRealtimeVideoSource::~MockRealtimeVideoSource()
@@ -191,10 +178,7 @@ void MockRealtimeVideoSource::updateSettings(RealtimeMediaSourceSettings& settin
 void MockRealtimeVideoSource::initializeCapabilities(RealtimeMediaSourceCapabilities& capabilities)
 {
     if (mockCamera()) {
-        if (device() == MockDevice::Camera1)
-            capabilities.addFacingMode(RealtimeMediaSourceSettings::User);
-        else
-            capabilities.addFacingMode(RealtimeMediaSourceSettings::Environment);
+        capabilities.addFacingMode(WTF::get<MockCameraProperties>(device().properties).facingModeCapability);
 
         capabilities.setWidth(CapabilityValueOrRange(320, 1920));
         capabilities.setHeight(CapabilityValueOrRange(240, 1080));
@@ -392,7 +376,7 @@ void MockRealtimeVideoSource::drawText(GraphicsContext& context)
         context.drawText(statsFont, TextRun((StringView(string))), statsLocation);
     } else {
         statsLocation.move(0, m_statsFontSize);
-        context.drawText(statsFont, TextRun((StringView(device() == MockDevice::Screen1 ? "Screen 1" : "Screen 2"))), statsLocation);
+        context.drawText(statsFont, TextRun { id() }, statsLocation);
     }
 
     FloatPoint bipBopLocation(size.width() * .6, size.height() * .6);
@@ -431,27 +415,7 @@ void MockRealtimeVideoSource::generateFrame()
     auto& size = this->size();
     FloatRect frameRect(FloatPoint(), size);
 
-    Color fillColor;
-    switch (device()) {
-    case MockDevice::Camera1:
-        fillColor = Color::black;
-        break;
-    case MockDevice::Camera2:
-        fillColor = Color::darkGray;
-        break;
-    case MockDevice::Screen1:
-        fillColor = Color::lightGray;
-        break;
-    case MockDevice::Screen2:
-        fillColor = Color::yellow;
-        break;
-    case MockDevice::Microphone1:
-    case MockDevice::Microphone2:
-    case MockDevice::Invalid:
-        ASSERT_NOT_REACHED();
-        break;
-    }
-
+    auto fillColor = mockCamera() ? WTF::get<MockCameraProperties>(device().properties).fillColor : WTF::get<MockDisplayProperties>(device().properties).fillColor;
     context.fillRect(FloatRect(FloatPoint(), size), fillColor);
 
     if (!muted()) {
index bd52682..a6589b8 100644 (file)
@@ -83,8 +83,8 @@ private:
 
     void delaySamples(Seconds) override;
 
-    bool mockCamera() const { return device() == MockDevice::Camera1 || device() == MockDevice::Camera2; }
-    bool mockScreen() const { return device() == MockDevice::Screen1 || device() == MockDevice::Screen2; }
+    bool mockCamera() const { return WTF::holds_alternative<MockCameraProperties>(device().properties); }
+    bool mockScreen() const { return WTF::holds_alternative<MockDisplayProperties>(device().properties); }
 
     float m_baseFontSize { 0 };
     float m_bipBopFontSize { 0 };
index bd9de34..422e524 100644 (file)
@@ -402,6 +402,7 @@ set(WebKit_SOURCES
     UIProcess/API/C/WKInspector.cpp
     UIProcess/API/C/WKKeyValueStorageManager.cpp
     UIProcess/API/C/WKMediaSessionMetadata.cpp
+    UIProcess/API/C/WKMockMediaDevice.cpp
     UIProcess/API/C/WKNavigationActionRef.cpp
     UIProcess/API/C/WKNavigationDataRef.cpp
     UIProcess/API/C/WKNavigationRef.cpp
index 52f976c..d5624d2 100644 (file)
@@ -1,3 +1,36 @@
+2018-06-25  Youenn Fablet  <youenn@apple.com>
+
+        Add API to control mock media devices
+        https://bugs.webkit.org/show_bug.cgi?id=186958
+
+        Reviewed by Eric Carlson.
+
+        Add API to clear, set, remove and reset mock media devices.
+        The mock media center of UIProcess and all WebProcesses are updated.
+
+        * CMakeLists.txt:
+        * UIProcess/API/C/WKMockMediaDevice.cpp: Added.
+        (typeFromString):
+        (WKAddMockMediaDevice):
+        (WKClearMockMediaDevices):
+        (WKRemoveMockMediaDevice):
+        (WKResetMockMediaDevices):
+        * UIProcess/API/C/WKMockMediaDevice.h: Added.
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::addMockMediaDevice):
+        (WebKit::WebProcessPool::clearMockMediaDevices):
+        (WebKit::WebProcessPool::removeMockMediaDevice):
+        (WebKit::WebProcessPool::resetMockMediaDevices):
+        * UIProcess/WebProcessPool.h:
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::addMockMediaDevice):
+        (WebKit::WebProcess::clearMockMediaDevices):
+        (WebKit::WebProcess::removeMockMediaDevice):
+        (WebKit::WebProcess::resetMockMediaDevices):
+        * WebProcess/WebProcess.h:
+        * WebProcess/WebProcess.messages.in:
+
 2018-06-25  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         [iPad apps on macOS] Click events are broken in WKWebView
diff --git a/Source/WebKit/UIProcess/API/C/WKMockMediaDevice.cpp b/Source/WebKit/UIProcess/API/C/WKMockMediaDevice.cpp
new file mode 100644 (file)
index 0000000..f276365
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+#include "WKMockMediaDevice.h"
+
+#include "WKAPICast.h"
+#include "WKString.h"
+#include "WebProcessPool.h"
+#include <WebCore/MockMediaDevice.h>
+
+using namespace WebKit;
+
+void WKAddMockMediaDevice(WKContextRef context, WKStringRef persistentId, WKStringRef label, WKStringRef type)
+{
+#if ENABLE(MEDIA_STREAM)
+    String typeString = WebKit::toImpl(type)->string();
+    Variant<WebCore::MockMicrophoneProperties, WebCore::MockCameraProperties, WebCore::MockDisplayProperties> properties;
+    if (typeString == "camera")
+        properties = WebCore::MockCameraProperties { };
+    else if (typeString == "screen")
+        properties = WebCore::MockDisplayProperties { };
+    else if (typeString != "microphone")
+        return;
+
+    toImpl(context)->addMockMediaDevice({ WebKit::toImpl(persistentId)->string(), WebKit::toImpl(label)->string(), WTFMove(properties) });
+#endif
+}
+
+void WKClearMockMediaDevices(WKContextRef context)
+{
+    toImpl(context)->clearMockMediaDevices();
+}
+
+void WKRemoveMockMediaDevice(WKContextRef context, WKStringRef persistentId)
+{
+    toImpl(context)->removeMockMediaDevice(WebKit::toImpl(persistentId)->string());
+}
+
+void WKResetMockMediaDevices(WKContextRef context)
+{
+    toImpl(context)->resetMockMediaDevices();
+}
diff --git a/Source/WebKit/UIProcess/API/C/WKMockMediaDevice.h b/Source/WebKit/UIProcess/API/C/WKMockMediaDevice.h
new file mode 100644 (file)
index 0000000..f776d28
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#pragma once
+
+#include <WebKit/WKBase.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+WK_EXPORT void WKAddMockMediaDevice(WKContextRef, WKStringRef persistentId, WKStringRef label, WKStringRef type);
+WK_EXPORT void WKClearMockMediaDevices(WKContextRef);
+WK_EXPORT void WKRemoveMockMediaDevice(WKContextRef, WKStringRef persistentId);
+WK_EXPORT void WKResetMockMediaDevices(WKContextRef);
+
+#ifdef __cplusplus
+}
+#endif
index bd377e4..72a83df 100644 (file)
@@ -82,6 +82,7 @@
 #include <JavaScriptCore/JSCInlines.h>
 #include <WebCore/ApplicationCacheStorage.h>
 #include <WebCore/LogInitialization.h>
+#include <WebCore/MockRealtimeMediaSourceCenter.h>
 #include <WebCore/NetworkStorageSession.h>
 #include <WebCore/PlatformScreen.h>
 #include <WebCore/Process.h>
@@ -2212,4 +2213,36 @@ void WebProcessPool::unregisterSuspendedPageProxy(SuspendedPageProxy& page)
         m_suspendedPages.remove(iterator);
 }
 
+void WebProcessPool::addMockMediaDevice(const MockMediaDevice& device)
+{
+#if ENABLE(MEDIA_STREAM)
+    MockRealtimeMediaSourceCenter::addDevice(device);
+    sendToAllProcesses(Messages::WebProcess::AddMockMediaDevice { device });
+#endif
+}
+
+void WebProcessPool::clearMockMediaDevices()
+{
+#if ENABLE(MEDIA_STREAM)
+    MockRealtimeMediaSourceCenter::setDevices({ });
+    sendToAllProcesses(Messages::WebProcess::ClearMockMediaDevices { });
+#endif
+}
+
+void WebProcessPool::removeMockMediaDevice(const String& persistentId)
+{
+#if ENABLE(MEDIA_STREAM)
+    MockRealtimeMediaSourceCenter::removeDevice(persistentId);
+    sendToAllProcesses(Messages::WebProcess::RemoveMockMediaDevice { persistentId });
+#endif
+}
+
+void WebProcessPool::resetMockMediaDevices()
+{
+#if ENABLE(MEDIA_STREAM)
+    MockRealtimeMediaSource::resetDevices();
+    sendToAllProcesses(Messages::WebProcess::ResetMockMediaDevices { });
+#endif
+}
+
 } // namespace WebKit
index aeb1910..83b063e 100644 (file)
@@ -82,6 +82,10 @@ class Navigation;
 class PageConfiguration;
 }
 
+namespace WebCore {
+struct MockMediaDevice;
+}
+
 namespace WebKit {
 
 class DownloadProxy;
@@ -454,6 +458,11 @@ public:
 
     void screenPropertiesStateChanged();
 
+    void addMockMediaDevice(const WebCore::MockMediaDevice&);
+    void clearMockMediaDevices();
+    void removeMockMediaDevice(const String& persistentId);
+    void resetMockMediaDevices();
+
 private:
     void platformInitialize();
 
index 6a50721..0863f99 100644 (file)
                410482CE1DDD324F00F006D0 /* RTCNetwork.h in Headers */ = {isa = PBXBuildFile; fileRef = 410482CC1DDD2FB500F006D0 /* RTCNetwork.h */; };
                4112B5551FA0EA7A00E67875 /* NetworkRTCResolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4112B5471F9FD3AB00E67875 /* NetworkRTCResolver.cpp */; };
                4112B5551FA0EA7A00E67986 /* NetworkRTCResolverCocoa.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4112B5471F9FD3AB00E67986 /* NetworkRTCResolverCocoa.cpp */; };
+               411A8DDB20DDD1AC0060D34F /* WKMockMediaDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 411A8DD920DDB6050060D34F /* WKMockMediaDevice.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               411A8DDC20DDD23F0060D34F /* WKMockMediaDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 411A8DDA20DDB6050060D34F /* WKMockMediaDevice.cpp */; };
                411B22641E371BA6004F7363 /* LibWebRTCNetwork.h in Headers */ = {isa = PBXBuildFile; fileRef = 411B22621E371244004F7363 /* LibWebRTCNetwork.h */; };
                413075A91DE85F2C0039EC69 /* NetworkRTCSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 413075981DE84FB00039EC69 /* NetworkRTCSocket.cpp */; };
                413075AA1DE85F300039EC69 /* NetworkRTCMonitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4130759A1DE84FB00039EC69 /* NetworkRTCMonitor.cpp */; };
                4112B5471F9FD3AB00E67986 /* NetworkRTCResolverCocoa.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NetworkRTCResolverCocoa.cpp; path = NetworkProcess/webrtc/NetworkRTCResolverCocoa.cpp; sourceTree = "<group>"; };
                4112B5481F9FD3AC00E67875 /* NetworkRTCResolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NetworkRTCResolver.h; path = NetworkProcess/webrtc/NetworkRTCResolver.h; sourceTree = "<group>"; };
                4112B5481F9FD3AC00E67986 /* NetworkRTCResolverCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NetworkRTCResolverCocoa.h; path = NetworkProcess/webrtc/NetworkRTCResolverCocoa.h; sourceTree = "<group>"; };
+               411A8DD920DDB6050060D34F /* WKMockMediaDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKMockMediaDevice.h; sourceTree = "<group>"; };
+               411A8DDA20DDB6050060D34F /* WKMockMediaDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WKMockMediaDevice.cpp; sourceTree = "<group>"; };
                411B22621E371244004F7363 /* LibWebRTCNetwork.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LibWebRTCNetwork.h; path = Network/webrtc/LibWebRTCNetwork.h; sourceTree = "<group>"; };
                413075981DE84FB00039EC69 /* NetworkRTCSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NetworkRTCSocket.cpp; path = NetworkProcess/webrtc/NetworkRTCSocket.cpp; sourceTree = "<group>"; };
                413075991DE84FB00039EC69 /* NetworkRTCSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NetworkRTCSocket.h; path = NetworkProcess/webrtc/NetworkRTCSocket.h; sourceTree = "<group>"; };
                                C98C48A81B6FD5B500145103 /* WKMediaSessionFocusManager.h */,
                                C9CD43991B4B024200239E33 /* WKMediaSessionMetadata.cpp */,
                                C9CD439A1B4B024200239E33 /* WKMediaSessionMetadata.h */,
+                               411A8DDA20DDB6050060D34F /* WKMockMediaDevice.cpp */,
+                               411A8DD920DDB6050060D34F /* WKMockMediaDevice.h */,
                                C09AE5E8125257C20025825D /* WKNativeEvent.h */,
                                2D3A65D81A7C3A1F00CAC637 /* WKNavigationActionRef.cpp */,
                                2D3A65D91A7C3A1F00CAC637 /* WKNavigationActionRef.h */,
                                C98C48AA1B6FD5B500145103 /* WKMediaSessionFocusManager.h in Headers */,
                                C9CD439D1B4B024F00239E33 /* WKMediaSessionMetadata.h in Headers */,
                                1AB40EE61BF677E300BA81BE /* WKMenuItemIdentifiersPrivate.h in Headers */,
+                               411A8DDB20DDD1AC0060D34F /* WKMockMediaDevice.h in Headers */,
                                BC4075FE124FF0270068F20A /* WKMutableArray.h in Headers */,
                                BC407600124FF0270068F20A /* WKMutableDictionary.h in Headers */,
                                C09AE5E9125257C20025825D /* WKNativeEvent.h in Headers */,
                        buildActionMask = 2147483647;
                        files = (
                        );
-                       inputFileListPaths = (
-                       );
                        inputPaths = (
                        );
                        name = "Unlock keychain";
-                       outputFileListPaths = (
-                       );
                        outputPaths = (
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        buildActionMask = 2147483647;
                        files = (
                        );
-                       inputFileListPaths = (
-                       );
                        inputPaths = (
                        );
                        name = "Unlock keychain";
-                       outputFileListPaths = (
-                       );
                        outputPaths = (
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        buildActionMask = 2147483647;
                        files = (
                        );
-                       inputFileListPaths = (
-                       );
                        inputPaths = (
                        );
                        name = "Remove stale entitlement file";
-                       outputFileListPaths = (
-                       );
                        outputPaths = (
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        buildActionMask = 2147483647;
                        files = (
                        );
-                       inputFileListPaths = (
-                       );
                        inputPaths = (
                        );
                        name = "Remove stale entitlement file";
-                       outputFileListPaths = (
-                       );
                        outputPaths = (
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        buildActionMask = 2147483647;
                        files = (
                        );
-                       inputFileListPaths = (
-                       );
                        inputPaths = (
                                "$(TEMP_FILE_DIR)/$(FULL_PRODUCT_NAME).xcent",
                        );
                        name = "Process WebContent entitlements";
-                       outputFileListPaths = (
-                       );
                        outputPaths = (
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        buildActionMask = 2147483647;
                        files = (
                        );
-                       inputFileListPaths = (
-                       );
                        inputPaths = (
                                "$(TEMP_FILE_DIR)/$(FULL_PRODUCT_NAME).xcent",
                        );
                        name = "Process WebContent entitlements";
-                       outputFileListPaths = (
-                       );
                        outputPaths = (
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                                C98C48A91B6FD5B500145103 /* WKMediaSessionFocusManager.cpp in Sources */,
                                C9CD439E1B4B025300239E33 /* WKMediaSessionMetadata.cpp in Sources */,
                                1AB40EE51BF677E300BA81BE /* WKMenuItemIdentifiers.mm in Sources */,
+                               411A8DDC20DDD23F0060D34F /* WKMockMediaDevice.cpp in Sources */,
                                BC4075FD124FF0270068F20A /* WKMutableArray.cpp in Sources */,
                                BC4075FF124FF0270068F20A /* WKMutableDictionary.cpp in Sources */,
                                1A5B1C501898606F004FCF9B /* WKNavigation.mm in Sources */,
index 8fa86ad..0066f11 100644 (file)
 #include <WebCore/MemoryCache.h>
 #include <WebCore/MemoryRelease.h>
 #include <WebCore/MessagePort.h>
+#include <WebCore/MockRealtimeMediaSourceCenter.h>
 #include <WebCore/NetworkStorageSession.h>
 #include <WebCore/Page.h>
 #include <WebCore/PageCache.h>
@@ -1690,4 +1691,26 @@ void WebProcess::setScreenProperties(const WebCore::ScreenProperties& properties
 }
 #endif
 
+#if ENABLE(MEDIA_STREAM)
+void WebProcess::addMockMediaDevice(const WebCore::MockMediaDevice& device)
+{
+    MockRealtimeMediaSourceCenter::addDevice(device);
+}
+
+void WebProcess::clearMockMediaDevices()
+{
+    MockRealtimeMediaSourceCenter::setDevices({ });
+}
+
+void WebProcess::removeMockMediaDevice(const String& persistentId)
+{
+    MockRealtimeMediaSourceCenter::removeDevice(persistentId);
+}
+
+void WebProcess::resetMockMediaDevices()
+{
+    MockRealtimeMediaSource::resetDevices();
+}
+#endif
+
 } // namespace WebKit
index b8520ac..88cc21f 100644 (file)
@@ -69,6 +69,7 @@ class ResourceRequest;
 class UserGestureToken;
 struct MessagePortIdentifier;
 struct MessageWithMessagePorts;
+struct MockMediaDevice;
 struct PluginInfo;
 struct SecurityOriginData;
 struct SoupNetworkProxySettings;
@@ -359,6 +360,13 @@ private:
     void stopRunLoop() override;
 #endif
 
+#if ENABLE(MEDIA_STREAM)
+    void addMockMediaDevice(const WebCore::MockMediaDevice&);
+    void clearMockMediaDevices();
+    void removeMockMediaDevice(const String& persistentId);
+    void resetMockMediaDevices();
+#endif
+
     void platformInitializeProcess(const ChildProcessInitializationParameters&);
 
     // IPC::Connection::Client
index 5ab522b..0db8bd9 100644 (file)
@@ -139,4 +139,11 @@ messages -> WebProcess LegacyReceiver {
 #if PLATFORM(COCOA)
     SetMediaMIMETypes(Vector<String> types)
 #endif
+
+#if ENABLE(MEDIA_STREAM)
+    AddMockMediaDevice(struct WebCore::MockMediaDevice device);
+    ClearMockMediaDevices();
+    RemoveMockMediaDevice(String persistentId);
+    ResetMockMediaDevices();
+#endif
 }
index 35bf228..c0e2b83 100644 (file)
@@ -1,3 +1,31 @@
+2018-06-25  Youenn Fablet  <youenn@apple.com>
+
+        Add API to control mock media devices
+        https://bugs.webkit.org/show_bug.cgi?id=186958
+
+        Reviewed by Eric Carlson.
+
+        Add test runner API to clear/add/remove/reset mock media devices.
+
+        * WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
+        * WebKitTestRunner/InjectedBundle/TestRunner.cpp:
+        (WTR::TestRunner::addMockMediaDevice):
+        (WTR::TestRunner::addMockCameraDevice):
+        (WTR::TestRunner::addMockMicrophoneDevice):
+        (WTR::TestRunner::addMockScreenDevice):
+        (WTR::TestRunner::clearMockMediaDevices):
+        (WTR::TestRunner::removeMockMediaDevice):
+        (WTR::TestRunner::resetMockMediaDevices):
+        * WebKitTestRunner/InjectedBundle/TestRunner.h:
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::addMockMediaDevice):
+        (WTR::TestController::clearMockMediaDevices):
+        (WTR::TestController::removeMockMediaDevice):
+        (WTR::TestController::resetMockMediaDevices):
+        * WebKitTestRunner/TestController.h:
+        * WebKitTestRunner/TestInvocation.cpp:
+        (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
+
 2018-06-25  Aakash Jain  <aakash_jain@apple.com>
 
         [ews-build] Add support for try Buildbot try schedulers
index 7e7659c..1791668 100644 (file)
@@ -336,4 +336,11 @@ interface TestRunner {
     void getApplicationManifestThen(object callback);
 
     void installFakeHelvetica(DOMString configuration);
+
+    void addMockCameraDevice(DOMString persistentId, DOMString label);
+    void addMockMicrophoneDevice(DOMString persistentId, DOMString label);
+    void addMockScreenDevice(DOMString persistentId, DOMString label);
+    void clearMockMediaDevices();
+    void removeMockMediaDevice(DOMString persistentId);
+    void resetMockMediaDevices();
 };
index 2831a87..95aaef4 100644 (file)
@@ -1971,6 +1971,71 @@ void TestRunner::callDidReceiveAllStorageAccessEntriesCallback(Vector<String>& d
     callTestRunnerCallback(AllStorageAccessEntriesCallbackID, 1, &result);
 }
 
+void TestRunner::addMockMediaDevice(JSStringRef persistentId, JSStringRef label, const char* type)
+{
+    Vector<WKRetainPtr<WKStringRef>> keys;
+    Vector<WKRetainPtr<WKTypeRef>> values;
+
+    keys.append({ AdoptWK, WKStringCreateWithUTF8CString("PersistentID") });
+    values.append(toWK(persistentId));
+
+    keys.append({ AdoptWK, WKStringCreateWithUTF8CString("Label") });
+    values.append(toWK(label));
+
+    keys.append({ AdoptWK, WKStringCreateWithUTF8CString("Type") });
+    values.append({ AdoptWK, WKStringCreateWithUTF8CString(type) });
+
+    Vector<WKStringRef> rawKeys;
+    Vector<WKTypeRef> rawValues;
+    rawKeys.resize(keys.size());
+    rawValues.resize(values.size());
+
+    for (size_t i = 0; i < keys.size(); ++i) {
+        rawKeys[i] = keys[i].get();
+        rawValues[i] = values[i].get();
+    }
+
+    WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("AddMockMediaDevice"));
+    WKRetainPtr<WKDictionaryRef> messageBody(AdoptWK, WKDictionaryCreate(rawKeys.data(), rawValues.data(), rawKeys.size()));
+
+    WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), nullptr);
+}
+
+void TestRunner::addMockCameraDevice(JSStringRef persistentId, JSStringRef label)
+{
+    addMockMediaDevice(persistentId, label, "camera");
+}
+
+void TestRunner::addMockMicrophoneDevice(JSStringRef persistentId, JSStringRef label)
+{
+    addMockMediaDevice(persistentId, label, "microphone");
+}
+
+void TestRunner::addMockScreenDevice(JSStringRef persistentId, JSStringRef label)
+{
+    addMockMediaDevice(persistentId, label, "screen");
+}
+
+void TestRunner::clearMockMediaDevices()
+{
+    WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("ClearMockMediaDevices"));
+    WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), nullptr, nullptr);
+}
+
+void TestRunner::removeMockMediaDevice(JSStringRef persistentId)
+{
+    WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("RemoveMockMediaDevice"));
+    WKRetainPtr<WKTypeRef> messageBody(toWK(persistentId));
+
+    WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), nullptr);
+}
+
+void TestRunner::resetMockMediaDevices()
+{
+    WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("ResetMockMediaDevices"));
+    WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), nullptr, nullptr);
+}
+
 #if PLATFORM(MAC)
 void TestRunner::connectMockGamepad(unsigned index)
 {
index f2901bc..789ba23 100644 (file)
@@ -446,6 +446,13 @@ public:
     void dumpAllHTTPRedirectedResponseHeaders() { m_dumpAllHTTPRedirectedResponseHeaders = true; }
     bool shouldDumpAllHTTPRedirectedResponseHeaders() const { return m_dumpAllHTTPRedirectedResponseHeaders; }
 
+    void addMockCameraDevice(JSStringRef persistentId, JSStringRef label);
+    void addMockMicrophoneDevice(JSStringRef persistentId, JSStringRef label);
+    void addMockScreenDevice(JSStringRef persistentId, JSStringRef label);
+    void clearMockMediaDevices();
+    void removeMockMediaDevice(JSStringRef persistentId);
+    void resetMockMediaDevices();
+
 private:
     TestRunner();
 
@@ -455,6 +462,8 @@ private:
     void setDumpPixels(bool);
     void setWaitUntilDone(bool);
 
+    void addMockMediaDevice(JSStringRef persistentId, JSStringRef label, const char* type);
+
     WKRetainPtr<WKURLRef> m_testURL; // Set by InjectedBundlePage once provisional load starts.
 
     bool m_shouldDumpAllFrameScrollPositions;
index ba85189..377ccfe 100644 (file)
@@ -43,6 +43,7 @@
 #include <WebKit/WKFrameHandleRef.h>
 #include <WebKit/WKFrameInfoRef.h>
 #include <WebKit/WKIconDatabase.h>
+#include <WebKit/WKMockMediaDevice.h>
 #include <WebKit/WKNavigationResponseRef.h>
 #include <WebKit/WKNotification.h>
 #include <WebKit/WKNotificationManager.h>
@@ -2866,6 +2867,26 @@ void TestController::statisticsResetToConsistentState()
     WKWebsiteDataStoreStatisticsResetToConsistentState(dataStore);
 }
 
+void TestController::addMockMediaDevice(WKStringRef persistentID, WKStringRef label, WKStringRef type)
+{
+    WKAddMockMediaDevice(platformContext(), persistentID, label, type);
+}
+
+void TestController::clearMockMediaDevices()
+{
+    WKClearMockMediaDevices(platformContext());
+}
+
+void TestController::removeMockMediaDevice(WKStringRef persistentID)
+{
+    WKRemoveMockMediaDevice(platformContext(), persistentID);
+}
+
+void TestController::resetMockMediaDevices()
+{
+    WKResetMockMediaDevices(platformContext());
+}
+
 #if !PLATFORM(COCOA)
 void TestController::platformAddTestOptions(TestOptions&) const
 {
index f337834..a48103f 100644 (file)
@@ -213,6 +213,11 @@ public:
     bool didReceiveServerRedirectForProvisionalNavigation() const { return m_didReceiveServerRedirectForProvisionalNavigation; }
     void clearDidReceiveServerRedirectForProvisionalNavigation() { m_didReceiveServerRedirectForProvisionalNavigation = false; }
 
+    void addMockMediaDevice(WKStringRef persistentID, WKStringRef label, WKStringRef type);
+    void clearMockMediaDevices();
+    void removeMockMediaDevice(WKStringRef persistentID);
+    void resetMockMediaDevices();
+
 private:
     WKRetainPtr<WKPageConfigurationRef> generatePageConfiguration(WKContextConfigurationRef);
     WKRetainPtr<WKContextConfigurationRef> generateContextConfiguration() const;
index 52475df..05c3ce6 100644 (file)
@@ -916,6 +916,39 @@ WKRetainPtr<WKTypeRef> TestInvocation::didReceiveSynchronousMessageFromInjectedB
         return nullptr;
     }
 
+    if (WKStringIsEqualToUTF8CString(messageName, "AddMockMediaDevice")) {
+        ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
+
+        WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
+        WKRetainPtr<WKStringRef> persistentIDKey(AdoptWK, WKStringCreateWithUTF8CString("PersistentID"));
+        WKRetainPtr<WKStringRef> labelKey(AdoptWK, WKStringCreateWithUTF8CString("Label"));
+        WKRetainPtr<WKStringRef> typeKey(AdoptWK, WKStringCreateWithUTF8CString("Type"));
+
+        auto persistentID = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, persistentIDKey.get()));
+        auto label = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, labelKey.get()));
+        auto type = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, typeKey.get()));
+
+        TestController::singleton().addMockMediaDevice(persistentID, label, type);
+        return nullptr;
+    }
+
+    if (WKStringIsEqualToUTF8CString(messageName, "ClearMockMediaDevices")) {
+        TestController::singleton().clearMockMediaDevices();
+        return nullptr;
+    }
+
+    if (WKStringIsEqualToUTF8CString(messageName, "RemoveMockMediaDevice")) {
+        WKStringRef persistentId = static_cast<WKStringRef>(messageBody);
+
+        TestController::singleton().removeMockMediaDevice(persistentId);
+        return nullptr;
+    }
+
+    if (WKStringIsEqualToUTF8CString(messageName, "ResetMockMediaDevices")) {
+        TestController::singleton().resetMockMediaDevices();
+        return nullptr;
+    }
+
 #if PLATFORM(MAC)
     if (WKStringIsEqualToUTF8CString(messageName, "ConnectMockGamepad")) {
         ASSERT(WKGetTypeID(messageBody) == WKUInt64GetTypeID());