Diff
Modified: trunk/LayoutTests/ChangeLog (288893 => 288894)
--- trunk/LayoutTests/ChangeLog 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/LayoutTests/ChangeLog 2022-02-01 18:34:18 UTC (rev 288894)
@@ -1,3 +1,15 @@
+2022-02-01 Eric Carlson <[email protected]>
+
+ [macOS] Expose screen and window capture state to WKWebView
+ https://bugs.webkit.org/show_bug.cgi?id=235838
+ <rdar://problem/88207924>
+
+ Reviewed by Youenn Fablet.
+
+ * fast/mediastream/get-display-media-muted.html: Update for new and renamed states.
+ * fast/mediastream/media-stream-page-muted-expected.txt: Ditto.
+ * fast/mediastream/media-stream-page-muted.html: Ditto.
+
2022-02-01 Gabriel Nava Marino <[email protected]>
null ptr deref in LayoutIntegrationLineLayout LineLayout::adjustForPagination and LineLayout::visualOverflowBoundingBoxRectFor
Modified: trunk/LayoutTests/fast/mediastream/get-display-media-muted.html (288893 => 288894)
--- trunk/LayoutTests/fast/mediastream/get-display-media-muted.html 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/LayoutTests/fast/mediastream/get-display-media-muted.html 2022-02-01 18:34:18 UTC (rev 288894)
@@ -41,7 +41,7 @@
let stream = await callGetDisplayMedia({ video: true });
let pageMediaState = internals.pageMediaState();
- assert_false(pageMediaState.includes('HasMutedDisplayCaptureDevice'), 'page state does not include HasMutedDisplayCaptureDevice');
+ assert_false(pageMediaState.includes('HasMutedScreenCaptureDevice'), 'page state does not include HasMutedScreenCaptureDevice');
assert_false(pageMediaState.includes('HasMutedVideoCaptureDevice'), 'page state does not include HasMutedVideoCaptureDevice');
assert_false(pageMediaState.includes('HasMutedAudioCaptureDevice'), 'page state does not include HasMutedAudioCaptureDevice');
@@ -58,14 +58,14 @@
assert_unreached(`Page state did not change from ${pageMediaState}`);
pageMediaState = internals.pageMediaState();
- assert_false(pageMediaState.includes('HasMutedDisplayCaptureDevice'), 'page state does not include HasMutedDisplayCaptureDevice');
- assert_true(pageMediaState.includes('HasActiveDisplayCaptureDevice'), 'page state includes HasActiveDisplayCaptureDevice');
+ assert_false(pageMediaState.includes('HasMutedScreenCaptureDevice'), 'page state does not include HasMutedScreenCaptureDevice');
+ assert_true(pageMediaState.includes('HasActiveScreenCaptureDevice'), 'page state includes HasActiveScreenCaptureDevice');
resolve()
}
pageMediaState = internals.pageMediaState();
- assert_true(pageMediaState.includes('HasMutedDisplayCaptureDevice'), 'page state includes HasMutedDisplayCaptureDevice');
- assert_false(pageMediaState.includes('HasActiveDisplayCaptureDevice'), 'page state does not include HasActiveDisplayCaptureDevice');
+ assert_true(pageMediaState.includes('HasMutedScreenCaptureDevice'), 'page state includes HasMutedScreenCaptureDevice');
+ assert_false(pageMediaState.includes('HasActiveScreenCaptureDevice'), 'page state does not include HasActiveScreenCaptureDevice');
internals.setMediaStreamTrackMuted(track, false)
}
Modified: trunk/LayoutTests/fast/mediastream/media-stream-page-muted-expected.txt (288893 => 288894)
--- trunk/LayoutTests/fast/mediastream/media-stream-page-muted-expected.txt 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/LayoutTests/fast/mediastream/media-stream-page-muted-expected.txt 2022-02-01 18:34:18 UTC (rev 288894)
@@ -16,8 +16,8 @@
PASS screenCaptureTrack.muted is true
PASS microphoneCaptureTrack.muted is false
PASS cameraCaptureTrack.muted is false
-PASS window.internals.pageMediaState().includes(HasMutedDisplayCaptureDevice) became true
-PASS window.internals.pageMediaState().includes(HasActiveDisplayCaptureDevice) became false
+PASS window.internals.pageMediaState().includes(HasMutedScreenCaptureDevice) became true
+PASS window.internals.pageMediaState().includes(HasActiveScreenCaptureDevice) became false
PASS window.internals.pageMediaState().includes(HasActiveAudioCaptureDevice) became true
PASS window.internals.pageMediaState().includes(HasMutedAudioCaptureDevice) became false
PASS window.internals.pageMediaState().includes(HasActiveAudioCaptureDevice) became true
@@ -29,8 +29,8 @@
PASS screenCaptureTrack.muted is true
PASS microphoneCaptureTrack.muted is true
PASS cameraCaptureTrack.muted is true
-PASS window.internals.pageMediaState().includes(HasMutedDisplayCaptureDevice) became true
-PASS window.internals.pageMediaState().includes(HasActiveDisplayCaptureDevice) became false
+PASS window.internals.pageMediaState().includes(HasMutedScreenCaptureDevice) became true
+PASS window.internals.pageMediaState().includes(HasActiveScreenCaptureDevice) became false
PASS window.internals.pageMediaState().includes(HasMutedAudioCaptureDevice) became true
PASS window.internals.pageMediaState().includes(HasActiveAudioCaptureDevice) became false
PASS window.internals.pageMediaState().includes(HasMutedVideoCaptureDevice) became true
@@ -42,8 +42,8 @@
PASS screenCaptureTrack.muted is true
PASS microphoneCaptureTrack.muted is false
PASS cameraCaptureTrack.muted is false
-PASS window.internals.pageMediaState().includes(HasMutedDisplayCaptureDevice) became true
-PASS window.internals.pageMediaState().includes(HasActiveDisplayCaptureDevice) became false
+PASS window.internals.pageMediaState().includes(HasMutedScreenCaptureDevice) became true
+PASS window.internals.pageMediaState().includes(HasActiveScreenCaptureDevice) became false
PASS window.internals.pageMediaState().includes(HasActiveAudioCaptureDevice) became true
PASS window.internals.pageMediaState().includes(HasMutedAudioCaptureDevice) became false
PASS window.internals.pageMediaState().includes(HasActiveAudioCaptureDevice) became true
@@ -54,8 +54,8 @@
PASS screenCaptureTrack.muted is false
PASS microphoneCaptureTrack.muted is false
PASS cameraCaptureTrack.muted is false
-PASS window.internals.pageMediaState().includes(HasActiveDisplayCaptureDevice) became true
-PASS window.internals.pageMediaState().includes(HasMutedDisplayCaptureDevice) became false
+PASS window.internals.pageMediaState().includes(HasActiveScreenCaptureDevice) became true
+PASS window.internals.pageMediaState().includes(HasMutedScreenCaptureDevice) became false
PASS window.internals.pageMediaState().includes(HasActiveAudioCaptureDevice) became true
PASS window.internals.pageMediaState().includes(HasMutedAudioCaptureDevice) became false
PASS window.internals.pageMediaState().includes(HasActiveAudioCaptureDevice) became true
Modified: trunk/LayoutTests/fast/mediastream/media-stream-page-muted.html (288893 => 288894)
--- trunk/LayoutTests/fast/mediastream/media-stream-page-muted.html 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/LayoutTests/fast/mediastream/media-stream-page-muted.html 2022-02-01 18:34:18 UTC (rev 288894)
@@ -114,7 +114,7 @@
shouldBeFalse("microphoneCaptureTrack.muted");
shouldBeFalse("cameraCaptureTrack.muted");
- await checkPageState("HasMutedDisplayCaptureDevice", "HasActiveDisplayCaptureDevice");
+ await checkPageState("HasMutedScreenCaptureDevice", "HasActiveScreenCaptureDevice");
await checkPageState("HasActiveAudioCaptureDevice", "HasMutedAudioCaptureDevice");
await checkPageState("HasActiveAudioCaptureDevice", "HasMutedAudioCaptureDevice");
}
@@ -133,7 +133,7 @@
shouldBeTrue("microphoneCaptureTrack.muted");
shouldBeTrue("cameraCaptureTrack.muted");
- await checkPageState("HasMutedDisplayCaptureDevice", "HasActiveDisplayCaptureDevice");
+ await checkPageState("HasMutedScreenCaptureDevice", "HasActiveScreenCaptureDevice");
await checkPageState("HasMutedAudioCaptureDevice", "HasActiveAudioCaptureDevice");
await checkPageState("HasMutedVideoCaptureDevice", "HasActiveVideoCaptureDevice");
}
@@ -152,7 +152,7 @@
shouldBeFalse("microphoneCaptureTrack.muted");
shouldBeFalse("cameraCaptureTrack.muted");
- await checkPageState("HasMutedDisplayCaptureDevice", "HasActiveDisplayCaptureDevice");
+ await checkPageState("HasMutedScreenCaptureDevice", "HasActiveScreenCaptureDevice");
await checkPageState("HasActiveAudioCaptureDevice", "HasMutedAudioCaptureDevice");
await checkPageState("HasActiveAudioCaptureDevice", "HasMutedAudioCaptureDevice");
}
@@ -170,7 +170,7 @@
shouldBeFalse("microphoneCaptureTrack.muted");
shouldBeFalse("cameraCaptureTrack.muted");
- await checkPageState("HasActiveDisplayCaptureDevice", "HasMutedDisplayCaptureDevice");
+ await checkPageState("HasActiveScreenCaptureDevice", "HasMutedScreenCaptureDevice");
await checkPageState("HasActiveAudioCaptureDevice", "HasMutedAudioCaptureDevice");
await checkPageState("HasActiveAudioCaptureDevice", "HasMutedAudioCaptureDevice");
}
Modified: trunk/Source/WebCore/ChangeLog (288893 => 288894)
--- trunk/Source/WebCore/ChangeLog 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/ChangeLog 2022-02-01 18:34:18 UTC (rev 288894)
@@ -1,3 +1,50 @@
+2022-02-01 Eric Carlson <[email protected]>
+
+ [macOS] Expose screen and window capture state to WKWebView
+ https://bugs.webkit.org/show_bug.cgi?id=235838
+ <rdar://problem/88207924>
+
+ Reviewed by Youenn Fablet.
+
+ Updated GetDisplayMediaWindowAndScreen API test.
+
+ * Modules/mediastream/MediaDevices.cpp:
+ (WebCore::toMediaDeviceInfoKind): Add SystemAudio.
+
+ * Modules/mediastream/MediaStreamTrack.cpp:
+ (WebCore::sourceCaptureState): Differentiate state of window and screen capture tracks.
+ (WebCore::MediaStreamTrack::updateToPageMutedState): New.
+ (WebCore::trackTypeForMediaProducerCaptureKind):
+ (WebCore::trackMatchesKind): Deleted.
+ * Modules/mediastream/MediaStreamTrack.h:
+
+ * Modules/mediastream/UserMediaRequest.cpp:
+ (WebCore::UserMediaRequest::mediaStreamDidFail): Include new source types.
+
+ * Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp:
+ (WebCore::LibWebRTCMediaEndpoint::addTrack): Ditto.
+ (WebCore::LibWebRTCMediaEndpoint::createSourceAndRTCTrack):
+
+ * page/MediaProducer.h:
+
+ * platform/mediarecorder/MediaRecorderPrivate.cpp:
+ (WebCore::MediaRecorderPrivate::selectTracks): Include new track types.
+
+ * platform/mediastream/CaptureDevice.h:
+ * platform/mediastream/RealtimeMediaSource.h:
+ * platform/mediastream/cocoa/DisplayCaptureSourceCocoa.cpp:
+ (WebCore::DisplayCaptureSourceCocoa::create): Ditto.
+
+ * platform/mediastream/mac/DisplayCaptureManagerCocoa.cpp:
+ (WebCore::DisplayCaptureManagerCocoa::captureDeviceWithPersistentID): Ditto.
+
+ * platform/mediastream/mac/ScreenCaptureKitCaptureSource.mm:
+ * platform/mock/MockRealtimeMediaSourceCenter.cpp:
+ * testing/Internals.cpp:
+ (WebCore::Internals::pageMediaState): Update with new and changed type names.
+ (WebCore::Internals::stopObservingRealtimeMediaSource): Include new track types.
+ (WebCore::Internals::observeMediaStreamTrack): Ditto.
+
2022-02-01 Gabriel Nava Marino <[email protected]>
null ptr deref in LayoutIntegrationLineLayout LineLayout::adjustForPagination and LineLayout::visualOverflowBoundingBoxRectFor
Modified: trunk/Source/WebCore/Modules/mediastream/MediaDevices.cpp (288893 => 288894)
--- trunk/Source/WebCore/Modules/mediastream/MediaDevices.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/Modules/mediastream/MediaDevices.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -281,6 +281,7 @@
case CaptureDevice::DeviceType::Screen:
case CaptureDevice::DeviceType::Window:
return MediaDeviceInfo::Kind::Videoinput;
+ case CaptureDevice::DeviceType::SystemAudio:
case CaptureDevice::DeviceType::Unknown:
ASSERT_NOT_REACHED();
}
Modified: trunk/Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp (288893 => 288894)
--- trunk/Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -436,14 +436,22 @@
return MediaProducerMediaState::HasActiveVideoCaptureDevice;
break;
case CaptureDevice::DeviceType::Screen:
+ if (source.muted())
+ return MediaProducerMediaState::HasMutedScreenCaptureDevice;
+ if (source.interrupted())
+ return MediaProducerMediaState::HasInterruptedScreenCaptureDevice;
+ if (source.isProducingData())
+ return MediaProducerMediaState::HasActiveScreenCaptureDevice;
+ break;
case CaptureDevice::DeviceType::Window:
if (source.muted())
- return MediaProducerMediaState::HasMutedDisplayCaptureDevice;
+ return MediaProducerMediaState::HasMutedWindowCaptureDevice;
if (source.interrupted())
- return MediaProducerMediaState::HasInterruptedDisplayCaptureDevice;
+ return MediaProducerMediaState::HasInterruptedWindowCaptureDevice;
if (source.isProducingData())
- return MediaProducerMediaState::HasActiveDisplayCaptureDevice;
+ return MediaProducerMediaState::HasActiveWindowCaptureDevice;
break;
+ case CaptureDevice::DeviceType::SystemAudio:
case CaptureDevice::DeviceType::Speaker:
case CaptureDevice::DeviceType::Unknown:
ASSERT_NOT_REACHED();
@@ -521,6 +529,7 @@
case CaptureDevice::DeviceType::Window:
m_private->setMuted(page->mutedState().contains(MediaProducerMutedState::ScreenCaptureIsMuted));
break;
+ case CaptureDevice::DeviceType::SystemAudio:
case CaptureDevice::DeviceType::Speaker:
case CaptureDevice::DeviceType::Unknown:
ASSERT_NOT_REACHED();
@@ -528,15 +537,20 @@
}
}
-static inline bool trackMatchesKind(RealtimeMediaSource::Type type, MediaProducerMediaCaptureKind kind)
+static MediaProducerMediaCaptureKind trackTypeForMediaProducerCaptureKind(RealtimeMediaSource::Type type)
{
- switch (kind) {
- case MediaProducerMediaCaptureKind::Audio:
- return type == RealtimeMediaSource::Type::Audio;
- case MediaProducerMediaCaptureKind::AudioVideo:
- return type != RealtimeMediaSource::Type::None;
- case MediaProducerMediaCaptureKind::Video:
- return type == RealtimeMediaSource::Type::Video;
+ switch (type) {
+ case RealtimeMediaSource::Type::Audio:
+ return MediaProducerMediaCaptureKind::Microphone;
+ case RealtimeMediaSource::Type::SystemAudio:
+ return MediaProducerMediaCaptureKind::SystemAudio;
+ case RealtimeMediaSource::Type::Video:
+ return MediaProducerMediaCaptureKind::Camera;
+ case RealtimeMediaSource::Type::Screen:
+ case RealtimeMediaSource::Type::Window:
+ return MediaProducerMediaCaptureKind::Display;
+ case RealtimeMediaSource::Type::None:
+ break;
}
RELEASE_ASSERT_NOT_REACHED();
}
@@ -545,8 +559,10 @@
{
bool didEndCapture = false;
for (auto* captureTrack : allCaptureTracks()) {
- if (captureTrack->document() != &document || !trackMatchesKind(captureTrack->privateTrack().type(), kind))
+ if (captureTrack->document() != &document)
continue;
+ if (kind != MediaProducerMediaCaptureKind::EveryKind && kind != trackTypeForMediaProducerCaptureKind(captureTrack->privateTrack().type()))
+ continue;
captureTrack->stopTrack(MediaStreamTrack::StopMode::PostEvent);
didEndCapture = true;
}
Modified: trunk/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp (288893 => 288894)
--- trunk/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -271,6 +271,15 @@
case RealtimeMediaSource::Type::Video:
typeDescription = "video";
break;
+ case RealtimeMediaSource::Type::Screen:
+ typeDescription = "screen";
+ break;
+ case RealtimeMediaSource::Type::Window:
+ typeDescription = "window";
+ break;
+ case RealtimeMediaSource::Type::SystemAudio:
+ typeDescription = "system audio";
+ break;
case RealtimeMediaSource::Type::None:
typeDescription = "unknown";
break;
Modified: trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp (288893 => 288894)
--- trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -204,6 +204,9 @@
source = WTFMove(videoSource);
break;
}
+ case RealtimeMediaSource::Type::Screen:
+ case RealtimeMediaSource::Type::Window:
+ case RealtimeMediaSource::Type::SystemAudio:
case RealtimeMediaSource::Type::None:
ASSERT_NOT_REACHED();
return false;
@@ -416,6 +419,7 @@
case RealtimeMediaSource::Type::None:
ASSERT_NOT_REACHED();
break;
+ case RealtimeMediaSource::Type::SystemAudio:
case RealtimeMediaSource::Type::Audio: {
auto audioSource = RealtimeOutgoingAudioSource::create(track.privateTrack());
rtcTrack = m_peerConnectionFactory->CreateAudioTrack(track.id().utf8().data(), audioSource.ptr());
@@ -422,6 +426,8 @@
source = WTFMove(audioSource);
break;
}
+ case RealtimeMediaSource::Type::Screen:
+ case RealtimeMediaSource::Type::Window:
case RealtimeMediaSource::Type::Video: {
auto videoSource = RealtimeOutgoingVideoSource::create(track.privateTrack());
rtcTrack = m_peerConnectionFactory->CreateVideoTrack(track.id().utf8().data(), videoSource.ptr());
Modified: trunk/Source/WebCore/PAL/pal/mac/ScreenCaptureKitSoftLink.h (288893 => 288894)
--- trunk/Source/WebCore/PAL/pal/mac/ScreenCaptureKitSoftLink.h 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/PAL/pal/mac/ScreenCaptureKitSoftLink.h 2022-02-01 18:34:18 UTC (rev 288894)
@@ -37,7 +37,7 @@
SOFT_LINK_CLASS_FOR_HEADER_WITH_AVAILABILITY(PAL, SCStreamConfiguration, API_AVAILABLE(macos(12.3)))
SOFT_LINK_CLASS_FOR_HEADER_WITH_AVAILABILITY(PAL, SCStream, API_AVAILABLE(macos(12.3)))
-SOFT_LINK_CONSTANT_MAY_FAIL_FOR_HEADER(PAL, ScreenCaptureKit, SCStreamFrameInfoStatusKey, NSString *)
+SOFT_LINK_CONSTANT_FOR_HEADER(PAL, ScreenCaptureKit, SCStreamFrameInfoStatusKey, NSString *)
#define SCStreamFrameInfoStatusKey get_ScreenCaptureKit_SCStreamFrameInfoStatusKey()
#endif // HAVE(SCREEN_CAPTURE_KIT)
Modified: trunk/Source/WebCore/PAL/pal/mac/ScreenCaptureKitSoftLink.mm (288893 => 288894)
--- trunk/Source/WebCore/PAL/pal/mac/ScreenCaptureKitSoftLink.mm 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/PAL/pal/mac/ScreenCaptureKitSoftLink.mm 2022-02-01 18:34:18 UTC (rev 288894)
@@ -37,6 +37,6 @@
SOFT_LINK_CLASS_FOR_SOURCE_OPTIONAL_WITH_EXPORT_AND_AVAILABILITY(PAL, ScreenCaptureKit, SCStreamConfiguration, PAL_EXPORT, API_AVAILABLE(macos(12.3)))
SOFT_LINK_CLASS_FOR_SOURCE_OPTIONAL_WITH_EXPORT_AND_AVAILABILITY(PAL, ScreenCaptureKit, SCStream, PAL_EXPORT, API_AVAILABLE(macos(12.3)))
-SOFT_LINK_CONSTANT_MAY_FAIL_FOR_SOURCE_WITH_EXPORT(PAL, ScreenCaptureKit, SCStreamFrameInfoStatusKey, NSString *, PAL_EXPORT)
+SOFT_LINK_CONSTANT_FOR_SOURCE_WITH_EXPORT(PAL, ScreenCaptureKit, SCStreamFrameInfoStatusKey, NSString *, PAL_EXPORT)
#endif // PLATFORM(MAC)
Modified: trunk/Source/WebCore/page/MediaProducer.h (288893 => 288894)
--- trunk/Source/WebCore/page/MediaProducer.h 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/page/MediaProducer.h 2022-02-01 18:34:18 UTC (rev 288894)
@@ -50,16 +50,24 @@
HasInterruptedAudioCaptureDevice = 1 << 15,
HasInterruptedVideoCaptureDevice = 1 << 16,
HasUserInteractedWithMediaElement = 1 << 17,
- HasActiveDisplayCaptureDevice = 1 << 18,
- HasMutedDisplayCaptureDevice = 1 << 19,
- HasInterruptedDisplayCaptureDevice = 1 << 20,
+ HasActiveScreenCaptureDevice = 1 << 18,
+ HasMutedScreenCaptureDevice = 1 << 19,
+ HasInterruptedScreenCaptureDevice = 1 << 20,
+ HasActiveWindowCaptureDevice = 1 << 21,
+ HasMutedWindowCaptureDevice = 1 << 22,
+ HasInterruptedWindowCaptureDevice = 1 << 23,
+ HasActiveSystemAudioCaptureDevice = 1 << 24,
+ HasMutedSystemAudioCaptureDevice = 1 << 25,
+ HasInterruptedSystemAudioCaptureDevice = 1 << 26,
};
using MediaProducerMediaStateFlags = OptionSet<MediaProducerMediaState>;
enum class MediaProducerMediaCaptureKind : uint8_t {
- Audio,
- Video,
- AudioVideo
+ Microphone,
+ Camera,
+ Display,
+ SystemAudio,
+ EveryKind,
};
enum class MediaProducerMutedState : uint8_t {
@@ -67,6 +75,8 @@
AudioCaptureIsMuted = 1 << 1,
VideoCaptureIsMuted = 1 << 2,
ScreenCaptureIsMuted = 1 << 3,
+ WindowCaptureIsMuted = 1 << 4,
+ SystemAudioCaptureIsMuted = 1 << 4,
};
using MediaProducerMutedStateFlags = OptionSet<MediaProducerMutedState>;
@@ -78,19 +88,46 @@
using MutedStateFlags = MediaProducerMutedStateFlags;
static constexpr MediaStateFlags IsNotPlaying = { };
- static constexpr MediaStateFlags AudioCaptureMask = { MediaState::HasActiveAudioCaptureDevice, MediaState::HasMutedAudioCaptureDevice, MediaState::HasInterruptedAudioCaptureDevice };
+ static constexpr MediaStateFlags MicrophoneCaptureMask = { MediaState::HasActiveAudioCaptureDevice, MediaState::HasMutedAudioCaptureDevice, MediaState::HasInterruptedAudioCaptureDevice };
static constexpr MediaStateFlags VideoCaptureMask = { MediaState::HasActiveVideoCaptureDevice, MediaState::HasMutedVideoCaptureDevice, MediaState::HasInterruptedVideoCaptureDevice };
- static constexpr MediaStateFlags DisplayCaptureMask = { MediaState::HasActiveDisplayCaptureDevice, MediaState::HasMutedDisplayCaptureDevice, MediaState::HasInterruptedDisplayCaptureDevice };
- static constexpr MediaStateFlags ActiveCaptureMask = { MediaState::HasActiveAudioCaptureDevice, MediaState::HasActiveVideoCaptureDevice, MediaState::HasActiveDisplayCaptureDevice };
- static constexpr MediaStateFlags MutedCaptureMask = { MediaState::HasMutedAudioCaptureDevice, MediaState::HasMutedVideoCaptureDevice, MediaState::HasMutedDisplayCaptureDevice };
- static constexpr MediaStateFlags MediaCaptureMask = { MediaState::HasActiveAudioCaptureDevice, MediaState::HasMutedAudioCaptureDevice, MediaState::HasInterruptedAudioCaptureDevice, MediaState::HasActiveVideoCaptureDevice, MediaState::HasMutedVideoCaptureDevice, MediaState::HasInterruptedVideoCaptureDevice, MediaState::HasActiveDisplayCaptureDevice, MediaState::HasMutedDisplayCaptureDevice, MediaState::HasInterruptedDisplayCaptureDevice };
+ static constexpr MediaStateFlags ScreenCaptureMask = { MediaState::HasActiveScreenCaptureDevice, MediaState::HasMutedScreenCaptureDevice, MediaState::HasInterruptedScreenCaptureDevice };
+ static constexpr MediaStateFlags WindowCaptureMask = { MediaState::HasActiveWindowCaptureDevice, MediaState::HasMutedWindowCaptureDevice, MediaState::HasInterruptedWindowCaptureDevice };
+ static constexpr MediaStateFlags ActiveDisplayCaptureMask = { MediaState::HasActiveScreenCaptureDevice, MediaState::HasActiveWindowCaptureDevice };
+ static constexpr MediaStateFlags MutedDisplayCaptureMask = { MediaState::HasMutedScreenCaptureDevice, MediaState::HasMutedWindowCaptureDevice };
+ static constexpr MediaStateFlags DisplayCaptureMask = { ActiveDisplayCaptureMask | MutedDisplayCaptureMask };
+
+ static constexpr MediaStateFlags SystemAudioCaptureMask = { MediaState::HasActiveSystemAudioCaptureDevice, MediaState::HasMutedSystemAudioCaptureDevice, MediaState::HasInterruptedSystemAudioCaptureDevice };
+
+ static constexpr MediaStateFlags ActiveCaptureMask = { MediaState::HasActiveAudioCaptureDevice, MediaState::HasActiveVideoCaptureDevice, MediaState::HasActiveScreenCaptureDevice, MediaState::HasActiveWindowCaptureDevice, MediaState::HasActiveSystemAudioCaptureDevice };
+ static constexpr MediaStateFlags MutedCaptureMask = { MediaState::HasMutedAudioCaptureDevice, MediaState::HasMutedVideoCaptureDevice, MediaState::HasMutedScreenCaptureDevice, MediaState::HasMutedWindowCaptureDevice, MediaState::HasMutedSystemAudioCaptureDevice };
+
+ static constexpr MediaStateFlags MediaCaptureMask = {
+ MediaState::HasActiveAudioCaptureDevice,
+ MediaState::HasMutedAudioCaptureDevice,
+ MediaState::HasInterruptedAudioCaptureDevice,
+ MediaState::HasActiveVideoCaptureDevice,
+ MediaState::HasMutedVideoCaptureDevice,
+ MediaState::HasInterruptedVideoCaptureDevice,
+ MediaState::HasActiveScreenCaptureDevice,
+ MediaState::HasMutedScreenCaptureDevice,
+ MediaState::HasInterruptedScreenCaptureDevice,
+ MediaState::HasActiveWindowCaptureDevice,
+ MediaState::HasMutedWindowCaptureDevice,
+ MediaState::HasInterruptedWindowCaptureDevice,
+ MediaState::HasActiveSystemAudioCaptureDevice,
+ MediaState::HasMutedSystemAudioCaptureDevice,
+ MediaState::HasInterruptedSystemAudioCaptureDevice
+ };
+ static constexpr MediaStateFlags IsCapturingAudioMask = { MicrophoneCaptureMask | SystemAudioCaptureMask };
+ static constexpr MediaStateFlags IsCapturingVideoMask = { VideoCaptureMask | DisplayCaptureMask };
+
static bool isCapturing(MediaStateFlags state) { return state.containsAny(ActiveCaptureMask) || state.containsAny(MutedCaptureMask); }
virtual MediaStateFlags mediaState() const = 0;
static constexpr MutedStateFlags AudioAndVideoCaptureIsMuted = { MutedState::AudioCaptureIsMuted, MutedState::VideoCaptureIsMuted };
- static constexpr MutedStateFlags MediaStreamCaptureIsMuted = { MutedState::AudioCaptureIsMuted, MutedState::VideoCaptureIsMuted, MutedState::ScreenCaptureIsMuted };
+ static constexpr MutedStateFlags MediaStreamCaptureIsMuted = { MutedState::AudioCaptureIsMuted, MutedState::VideoCaptureIsMuted, MutedState::ScreenCaptureIsMuted, MutedState::WindowCaptureIsMuted, MutedState::SystemAudioCaptureIsMuted };
virtual void pageMutedStateDidChange() = 0;
@@ -105,9 +142,11 @@
template<> struct EnumTraits<WebCore::MediaProducerMediaCaptureKind> {
using values = EnumValues<
WebCore::MediaProducerMediaCaptureKind,
- WebCore::MediaProducerMediaCaptureKind::Audio,
- WebCore::MediaProducerMediaCaptureKind::Video,
- WebCore::MediaProducerMediaCaptureKind::AudioVideo
+ WebCore::MediaProducerMediaCaptureKind::Microphone,
+ WebCore::MediaProducerMediaCaptureKind::Camera,
+ WebCore::MediaProducerMediaCaptureKind::Display,
+ WebCore::MediaProducerMediaCaptureKind::SystemAudio,
+ WebCore::MediaProducerMediaCaptureKind::EveryKind
>;
};
@@ -132,9 +171,15 @@
WebCore::MediaProducerMediaState::HasInterruptedAudioCaptureDevice,
WebCore::MediaProducerMediaState::HasInterruptedVideoCaptureDevice,
WebCore::MediaProducerMediaState::HasUserInteractedWithMediaElement,
- WebCore::MediaProducerMediaState::HasActiveDisplayCaptureDevice,
- WebCore::MediaProducerMediaState::HasMutedDisplayCaptureDevice,
- WebCore::MediaProducerMediaState::HasInterruptedDisplayCaptureDevice
+ WebCore::MediaProducerMediaState::HasActiveScreenCaptureDevice,
+ WebCore::MediaProducerMediaState::HasMutedScreenCaptureDevice,
+ WebCore::MediaProducerMediaState::HasInterruptedScreenCaptureDevice,
+ WebCore::MediaProducerMediaState::HasActiveWindowCaptureDevice,
+ WebCore::MediaProducerMediaState::HasMutedWindowCaptureDevice,
+ WebCore::MediaProducerMediaState::HasInterruptedWindowCaptureDevice,
+ WebCore::MediaProducerMediaState::HasActiveSystemAudioCaptureDevice,
+ WebCore::MediaProducerMediaState::HasMutedSystemAudioCaptureDevice,
+ WebCore::MediaProducerMediaState::HasInterruptedSystemAudioCaptureDevice
>;
};
@@ -144,7 +189,9 @@
WebCore::MediaProducerMutedState::AudioIsMuted,
WebCore::MediaProducerMutedState::AudioCaptureIsMuted,
WebCore::MediaProducerMutedState::VideoCaptureIsMuted,
- WebCore::MediaProducerMutedState::ScreenCaptureIsMuted
+ WebCore::MediaProducerMutedState::ScreenCaptureIsMuted,
+ WebCore::MediaProducerMutedState::WindowCaptureIsMuted,
+ WebCore::MediaProducerMutedState::SystemAudioCaptureIsMuted
>;
};
Modified: trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivate.cpp (288893 => 288894)
--- trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivate.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/platform/mediarecorder/MediaRecorderPrivate.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -44,6 +44,8 @@
if (track.ended())
return;
switch (track.type()) {
+ case RealtimeMediaSource::Type::Screen:
+ case RealtimeMediaSource::Type::Window:
case RealtimeMediaSource::Type::Video: {
auto& settings = track.settings();
if (!selectedTracks.videoTrack && settings.supportsWidth() && settings.supportsHeight())
@@ -51,6 +53,7 @@
break;
}
case RealtimeMediaSource::Type::Audio:
+ case RealtimeMediaSource::Type::SystemAudio:
if (!selectedTracks.audioTrack)
selectedTracks.audioTrack = &track;
break;
Modified: trunk/Source/WebCore/platform/mediastream/CaptureDevice.h (288893 => 288894)
--- trunk/Source/WebCore/platform/mediastream/CaptureDevice.h 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/platform/mediastream/CaptureDevice.h 2022-02-01 18:34:18 UTC (rev 288894)
@@ -31,7 +31,7 @@
class CaptureDevice {
public:
- enum class DeviceType { Unknown, Microphone, Speaker, Camera, Screen, Window };
+ enum class DeviceType { Unknown, Microphone, Speaker, Camera, Screen, Window, SystemAudio };
CaptureDevice(const String& persistentId, DeviceType type, const String& label, const String& groupId = emptyString(), bool isEnabled = false, bool isDefault = false, bool isMock = false)
: m_persistentId(persistentId)
@@ -191,7 +191,8 @@
WebCore::CaptureDevice::DeviceType::Speaker,
WebCore::CaptureDevice::DeviceType::Camera,
WebCore::CaptureDevice::DeviceType::Screen,
- WebCore::CaptureDevice::DeviceType::Window
+ WebCore::CaptureDevice::DeviceType::Window,
+ WebCore::CaptureDevice::DeviceType::SystemAudio
>;
};
Modified: trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h (288893 => 288894)
--- trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h 2022-02-01 18:34:18 UTC (rev 288894)
@@ -115,7 +115,7 @@
const String& persistentID() const { return m_persistentID; }
- enum class Type { None, Audio, Video };
+ enum class Type { None, Audio, Video, Screen, Window, SystemAudio };
Type type() const { return m_type; }
virtual void whenReady(CompletionHandler<void(String)>&&);
Modified: trunk/Source/WebCore/platform/mediastream/cocoa/DisplayCaptureSourceCocoa.cpp (288893 => 288894)
--- trunk/Source/WebCore/platform/mediastream/cocoa/DisplayCaptureSourceCocoa.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/platform/mediastream/cocoa/DisplayCaptureSourceCocoa.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -78,6 +78,7 @@
return create(ScreenCaptureKitCaptureSource::create(device, constraints), device, WTFMove(hashSalt), constraints);
#endif
break;
+ case CaptureDevice::DeviceType::SystemAudio:
case CaptureDevice::DeviceType::Microphone:
case CaptureDevice::DeviceType::Speaker:
case CaptureDevice::DeviceType::Camera:
Modified: trunk/Source/WebCore/platform/mediastream/mac/DisplayCaptureManagerCocoa.cpp (288893 => 288894)
--- trunk/Source/WebCore/platform/mediastream/mac/DisplayCaptureManagerCocoa.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/platform/mediastream/mac/DisplayCaptureManagerCocoa.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -129,6 +129,7 @@
return windowCaptureDeviceWithPersistentID(id);
break;
+ case CaptureDevice::DeviceType::SystemAudio:
case CaptureDevice::DeviceType::Camera:
case CaptureDevice::DeviceType::Microphone:
case CaptureDevice::DeviceType::Speaker:
Modified: trunk/Source/WebCore/platform/mediastream/mac/ScreenCaptureKitCaptureSource.mm (288893 => 288894)
--- trunk/Source/WebCore/platform/mediastream/mac/ScreenCaptureKitCaptureSource.mm 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/platform/mediastream/mac/ScreenCaptureKitCaptureSource.mm 2022-02-01 18:34:18 UTC (rev 288894)
@@ -55,10 +55,6 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
-@interface SCStream (SCStream_Deprecated)
-- (void)startCaptureWithCFrameHandler:(SCStreamBufferFrameAvailableHandler)frameHandler completionHandler:(void (^)(NSError *error))completionHandler;
-@end
-
using namespace WebCore;
@interface WebCoreScreenCaptureKitHelper : NSObject<SCStreamDelegate> {
WeakPtr<ScreenCaptureKitCaptureSource> _callback;
Modified: trunk/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp (288893 => 288894)
--- trunk/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -196,6 +196,7 @@
case CaptureDevice::DeviceType::Microphone:
case CaptureDevice::DeviceType::Speaker:
case CaptureDevice::DeviceType::Camera:
+ case CaptureDevice::DeviceType::SystemAudio:
case CaptureDevice::DeviceType::Unknown:
ASSERT_NOT_REACHED();
break;
Modified: trunk/Source/WebCore/testing/Internals.cpp (288893 => 288894)
--- trunk/Source/WebCore/testing/Internals.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebCore/testing/Internals.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -4736,21 +4736,43 @@
string.append("HasPlaybackTargetAvailabilityListener,");
if (state.containsAny(MediaProducerMediaState::HasAudioOrVideo))
string.append("HasAudioOrVideo,");
+
if (state.containsAny(MediaProducerMediaState::HasActiveAudioCaptureDevice))
string.append("HasActiveAudioCaptureDevice,");
+ if (state.containsAny(MediaProducerMediaState::HasMutedAudioCaptureDevice))
+ string.append("HasMutedAudioCaptureDevice,");
+ if (state.containsAny(MediaProducerMediaState::HasInterruptedAudioCaptureDevice))
+ string.append("HasInterruptedAudioCaptureDevice,");
+
if (state.containsAny(MediaProducerMediaState::HasActiveVideoCaptureDevice))
string.append("HasActiveVideoCaptureDevice,");
- if (state.containsAny(MediaProducerMediaState::HasMutedAudioCaptureDevice))
- string.append("HasMutedAudioCaptureDevice,");
if (state.containsAny(MediaProducerMediaState::HasMutedVideoCaptureDevice))
string.append("HasMutedVideoCaptureDevice,");
+ if (state.containsAny(MediaProducerMediaState::HasInterruptedVideoCaptureDevice))
+ string.append("HasInterruptedVideoCaptureDevice,");
+
if (state.containsAny(MediaProducerMediaState::HasUserInteractedWithMediaElement))
string.append("HasUserInteractedWithMediaElement,");
- if (state.containsAny(MediaProducerMediaState::HasActiveDisplayCaptureDevice))
- string.append("HasActiveDisplayCaptureDevice,");
- if (state.containsAny(MediaProducerMediaState::HasMutedDisplayCaptureDevice))
- string.append("HasMutedDisplayCaptureDevice,");
+ if (state.containsAny(MediaProducerMediaState::HasActiveScreenCaptureDevice))
+ string.append("HasActiveScreenCaptureDevice,");
+ if (state.containsAny(MediaProducerMediaState::HasMutedScreenCaptureDevice))
+ string.append("HasMutedScreenCaptureDevice,");
+
+ if (state.containsAny(MediaProducerMediaState::HasActiveWindowCaptureDevice))
+ string.append("HasActiveWindowCaptureDevice,");
+ if (state.containsAny(MediaProducerMediaState::HasMutedWindowCaptureDevice))
+ string.append("HasMutedWindowCaptureDevice,");
+ if (state.containsAny(MediaProducerMediaState::HasInterruptedWindowCaptureDevice))
+ string.append("HasInterruptedWindowCaptureDevice,");
+
+ if (state.containsAny(MediaProducerMediaState::HasActiveSystemAudioCaptureDevice))
+ string.append("HasActiveSystemAudioCaptureDevice,");
+ if (state.containsAny(MediaProducerMediaState::HasMutedSystemAudioCaptureDevice))
+ string.append("HasMutedSystemAudioCaptureDevice,");
+ if (state.containsAny(MediaProducerMediaState::HasInterruptedSystemAudioCaptureDevice))
+ string.append("HasInterruptedSystemAudioCaptureDevice,");
+
if (string.isEmpty())
string.append("IsNotPlaying");
else
@@ -5442,9 +5464,12 @@
switch (m_trackSource->type()) {
case RealtimeMediaSource::Type::Audio:
+ case RealtimeMediaSource::Type::SystemAudio:
m_trackSource->removeAudioSampleObserver(*this);
break;
case RealtimeMediaSource::Type::Video:
+ case RealtimeMediaSource::Type::Screen:
+ case RealtimeMediaSource::Type::Window:
m_trackSource->removeVideoSampleObserver(*this);
break;
case RealtimeMediaSource::Type::None:
@@ -5465,9 +5490,12 @@
m_trackSource->addObserver(*this);
switch (m_trackSource->type()) {
case RealtimeMediaSource::Type::Audio:
+ case RealtimeMediaSource::Type::SystemAudio:
m_trackSource->addAudioSampleObserver(*this);
break;
case RealtimeMediaSource::Type::Video:
+ case RealtimeMediaSource::Type::Screen:
+ case RealtimeMediaSource::Type::Window:
m_trackSource->addVideoSampleObserver(*this);
break;
case RealtimeMediaSource::Type::None:
Modified: trunk/Source/WebKit/ChangeLog (288893 => 288894)
--- trunk/Source/WebKit/ChangeLog 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/ChangeLog 2022-02-01 18:34:18 UTC (rev 288894)
@@ -1,3 +1,81 @@
+2022-02-01 Eric Carlson <[email protected]>
+
+ [macOS] Expose screen and window capture state to WKWebView
+ https://bugs.webkit.org/show_bug.cgi?id=235838
+ <rdar://problem/88207924>
+
+ Reviewed by Youenn Fablet.
+
+ * GPUProcess/GPUConnectionToWebProcess.cpp:
+ * UIProcess/API/C/WKPage.cpp:
+ (WKPageGetMediaState): Rename *DisplayCaptureDevice to *ScreenCaptureDevice. Add enums
+ for window capture.
+
+ * UIProcess/API/C/WKPagePrivate.h:
+ * UIProcess/API/Cocoa/WKWebView.h: Drive by: fix typo in header doc.
+
+ * UIProcess/API/Cocoa/WKWebView.mm:
+ (-[WKWebView setMicrophoneCaptureState:completionHandler:]): MediaProducerMediaCaptureKind::Audio
+ was renamed to MediaProducerMediaCaptureKind::Microphone.
+ (-[WKWebView setCameraCaptureState:completionHandler:]): MediaProducerMediaCaptureKind::Video
+ was renamed to MediaProducerMediaCaptureKind::Camera.
+ (-[WKWebView _stopMediaCapture]): stopMediaCapture takes an optionSet.
+ (-[WKWebView _displayCaptureSurfaces]): Expose the type(s) of displays surfaces
+ being captured.
+ (-[WKWebView _displayCaptureState]): Expose the display capture state.
+ (-[WKWebView _systemAudioCaptureState]): Expose the system audio capture state.
+ (-[WKWebView _setDisplayCaptureState:completionHandler:]): Change the display
+ capture state.
+ (-[WKWebView _setSystemAudioCaptureState:completionHandler:]): Change the system
+ audio capture state.
+ * UIProcess/API/Cocoa/WKWebViewPrivate.h:
+
+ * UIProcess/API/glib/WebKitWebView.cpp:
+ (webkitWebViewMediaCaptureStateDidChange): MediaProducer::AudioCaptureMask ->
+ MediaProducer::MicrophoneCaptureMask.
+ (webkitWebViewConfigureMediaCapture): Update for MediaProducerMediaCaptureKind changes.
+ (webkit_web_view_set_microphone_capture_state): Ditto.
+ (webkit_web_view_set_camera_capture_state): MediaProducerMediaCaptureKind::Video ->
+ MediaProducerMediaCaptureKind::Camera
+ (webkit_web_view_set_display_capture_state): Ditto.
+ (webkit_web_view_get_display_capture_state): MediaState::HasActiveDisplayCaptureDevice ->
+ MediaState::HasActiveScreenCaptureDevice. MediaState::HasMutedDisplayCaptureDevice ->
+ MediaState::HasMutedScreenCaptureDevice
+
+ * UIProcess/Cocoa/PageClientImplCocoa.h:
+ * UIProcess/Cocoa/PageClientImplCocoa.mm:
+ (WebKit::PageClientImplCocoa::displayCaptureWillChange): KVO support.
+ (WebKit::PageClientImplCocoa::displayCaptureSurfacesWillChange): Ditto.
+ (WebKit::PageClientImplCocoa::systemAudioCaptureWillChange): Ditto.
+ (WebKit::PageClientImplCocoa::displayCaptureChanged): Ditto.
+ (WebKit::PageClientImplCocoa::displayCaptureSurfacesChanged): Ditto.
+ (WebKit::PageClientImplCocoa::systemAudioCaptureChanged): Ditto.
+
+ * UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp:
+ (WebKit::UserMediaCaptureManagerProxy::SourceProxy::SourceProxy): Support new types.
+ (WebKit::UserMediaCaptureManagerProxy::SourceProxy::~SourceProxy): Ditto.
+ (WebKit::UserMediaCaptureManagerProxy::createMediaSourceForCaptureDeviceWithConstraints): Ditto.
+
+ * UIProcess/PageClient.h:
+ (WebKit::PageClient::displayCaptureWillChange):
+ (WebKit::PageClient::displayCaptureSurfacesWillChange):
+ (WebKit::PageClient::systemAudioCaptureWillChange):
+ (WebKit::PageClient::displayCaptureChanged):
+ (WebKit::PageClient::displayCaptureSurfacesChanged):
+ (WebKit::PageClient::systemAudioCaptureChanged):
+
+ * UIProcess/UserMediaPermissionRequestManagerProxy.cpp:
+ (WebKit::UserMediaPermissionRequestManagerProxy::~UserMediaPermissionRequestManagerProxy):
+ MediaProducerMediaCaptureKind::AudioVideo -> MediaProducerMediaCaptureKind::EveryKind.
+ (WebKit::UserMediaPermissionRequestManagerProxy::stopCapture): Ditto.
+
+ * UIProcess/WebPageProxy.cpp:
+ (WebKit::WebPageProxy::updateReportedMediaCaptureState): Deal with new types.
+ * UIProcess/WebPageProxy.h:
+
+ * WebProcess/WebProcess.cpp:
+ (WebKit::checkDocumentsCaptureStateConsistency):
+
2022-02-01 Simon Fraser <[email protected]>
Minor cleanup in RemoteLayerBackingStoreCollection
Modified: trunk/Source/WebKit/GPUProcess/GPUConnectionToWebProcess.cpp (288893 => 288894)
--- trunk/Source/WebKit/GPUProcess/GPUConnectionToWebProcess.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/GPUProcess/GPUConnectionToWebProcess.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -173,6 +173,7 @@
bool willStartCapture(CaptureDevice::DeviceType type) const final
{
switch (type) {
+ case CaptureDevice::DeviceType::SystemAudio:
case CaptureDevice::DeviceType::Unknown:
case CaptureDevice::DeviceType::Speaker:
return false;
Modified: trunk/Source/WebKit/UIProcess/API/C/WKPage.cpp (288893 => 288894)
--- trunk/Source/WebKit/UIProcess/API/C/WKPage.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/UIProcess/API/C/WKPage.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -2966,10 +2966,14 @@
state |= kWKMediaHasMutedAudioCaptureDevice;
if (coreState & WebCore::MediaProducerMediaState::HasMutedVideoCaptureDevice)
state |= kWKMediaHasMutedVideoCaptureDevice;
- if (coreState & WebCore::MediaProducerMediaState::HasActiveDisplayCaptureDevice)
- state |= kWKMediaHasActiveDisplayCaptureDevice;
- if (coreState & WebCore::MediaProducerMediaState::HasMutedDisplayCaptureDevice)
- state |= kWKMediaHasMutedDisplayCaptureDevice;
+ if (coreState & WebCore::MediaProducerMediaState::HasActiveScreenCaptureDevice)
+ state |= kWKMediaHasActiveScreenCaptureDevice;
+ if (coreState & WebCore::MediaProducerMediaState::HasMutedScreenCaptureDevice)
+ state |= kWKMediaHasMutedScreenCaptureDevice;
+ if (coreState & WebCore::MediaProducerMediaState::HasActiveWindowCaptureDevice)
+ state |= kWKMediaHasActiveWindowCaptureDevice;
+ if (coreState & WebCore::MediaProducerMediaState::HasMutedWindowCaptureDevice)
+ state |= kWKMediaHasMutedWindowCaptureDevice;
return state;
}
Modified: trunk/Source/WebKit/UIProcess/API/C/WKPagePrivate.h (288893 => 288894)
--- trunk/Source/WebKit/UIProcess/API/C/WKPagePrivate.h 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/UIProcess/API/C/WKPagePrivate.h 2022-02-01 18:34:18 UTC (rev 288894)
@@ -146,8 +146,12 @@
kWKMediaHasActiveVideoCaptureDevice = 1 << 3,
kWKMediaHasMutedAudioCaptureDevice = 1 << 4,
kWKMediaHasMutedVideoCaptureDevice = 1 << 5,
- kWKMediaHasActiveDisplayCaptureDevice = 1 << 6,
- kWKMediaHasMutedDisplayCaptureDevice = 1 << 7,
+ kWKMediaHasActiveScreenCaptureDevice = 1 << 6,
+ kWKMediaHasActiveDisplayCaptureDevice = kWKMediaHasActiveScreenCaptureDevice,
+ kWKMediaHasMutedScreenCaptureDevice = 1 << 7,
+ kWKMediaHasMutedDisplayCaptureDevice = kWKMediaHasMutedScreenCaptureDevice,
+ kWKMediaHasActiveWindowCaptureDevice = 1 << 8,
+ kWKMediaHasMutedWindowCaptureDevice = 1 << 9,
};
typedef uint32_t WKMediaState;
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.h (288893 => 288894)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.h 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.h 2022-02-01 18:34:18 UTC (rev 288894)
@@ -406,7 +406,7 @@
/*! @abstract Set microphone capture state of a WKWebView.
@param state state to apply for capture.
- @param completionHandler A block to invoke after the camera state has been changed.
+ @param completionHandler A block to invoke after the microphone state has been changed.
@discussion
If value is WKMediaCaptureStateNone, this will stop any microphone capture.
If value is WKMediaCaptureStateMuted, any active microphone capture will become muted.
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm (288893 => 288894)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm 2022-02-01 18:34:18 UTC (rev 288894)
@@ -1069,7 +1069,7 @@
completionHandler = [] { };
if (state == WKMediaCaptureStateNone) {
- _page->stopMediaCapture(WebCore::MediaProducerMediaCaptureKind::Audio, [completionHandler = makeBlockPtr(completionHandler)] {
+ _page->stopMediaCapture(WebCore::MediaProducerMediaCaptureKind::Microphone, [completionHandler = makeBlockPtr(completionHandler)] {
completionHandler();
});
return;
@@ -1091,7 +1091,7 @@
completionHandler = [] { };
if (state == WKMediaCaptureStateNone) {
- _page->stopMediaCapture(WebCore::MediaProducerMediaCaptureKind::Video, [completionHandler = makeBlockPtr(completionHandler)] {
+ _page->stopMediaCapture(WebCore::MediaProducerMediaCaptureKind::Camera, [completionHandler = makeBlockPtr(completionHandler)] {
completionHandler();
});
return;
@@ -2332,7 +2332,7 @@
- (void)_stopMediaCapture
{
THROW_IF_SUSPENDED;
- _page->stopMediaCapture(WebCore::MediaProducerMediaCaptureKind::AudioVideo);
+ _page->stopMediaCapture(WebCore::MediaProducerMediaCaptureKind::EveryKind);
}
- (void)_stopAllMediaPlayback
@@ -3706,6 +3706,83 @@
[self _internalDoAfterNextPresentationUpdate:updateBlock withoutWaitingForPainting:YES withoutWaitingForAnimatedResize:NO];
}
+- (WKDisplayCaptureSurfaces) _displayCaptureSurfaces
+{
+ auto pageState = _page->reportedMediaState();
+ WKDisplayCaptureSurfaces state = WKDisplayCaptureSurfaceNone;
+ if (pageState.containsAny(WebCore::MediaProducer::ScreenCaptureMask))
+ state |= WKDisplayCaptureSurfaceScreen;
+ if (pageState.containsAny(WebCore::MediaProducer::WindowCaptureMask))
+ state |= WKDisplayCaptureSurfaceWindow;
+ return state;
+}
+
+- (WKDisplayCaptureState) _displayCaptureState
+{
+ auto state = _page->reportedMediaState();
+ if (state & WebCore::MediaProducer::ActiveDisplayCaptureMask)
+ return WKDisplayCaptureStateActive;
+ if (state & WebCore::MediaProducer::MutedDisplayCaptureMask)
+ return WKDisplayCaptureStateMuted;
+ return WKDisplayCaptureStateNone;
+}
+
+- (WKSystemAudioCaptureState)_systemAudioCaptureState
+{
+ auto state = _page->reportedMediaState();
+ if (state & WebCore::MediaProducerMediaState::HasActiveSystemAudioCaptureDevice)
+ return WKSystemAudioCaptureStateActive;
+ if (state & WebCore::MediaProducerMediaState::HasMutedSystemAudioCaptureDevice)
+ return WKSystemAudioCaptureStateMuted;
+ return WKSystemAudioCaptureStateNone;
+}
+
+- (void)_setDisplayCaptureState:(WKDisplayCaptureState)state completionHandler:(void (^)(void))completionHandler
+{
+ THROW_IF_SUSPENDED;
+ if (!completionHandler)
+ completionHandler = [] { };
+
+ if (state == WKDisplayCaptureStateNone) {
+ _page->stopMediaCapture(WebCore::MediaProducerMediaCaptureKind::Display, [completionHandler = makeBlockPtr(completionHandler)] {
+ completionHandler();
+ });
+ return;
+ }
+
+ constexpr WebCore::MediaProducer::MutedStateFlags displayMutedFlags = { WebCore::MediaProducer::MutedState::ScreenCaptureIsMuted, WebCore::MediaProducer::MutedState::WindowCaptureIsMuted };
+ auto mutedState = _page->mutedStateFlags();
+ if (state == WKDisplayCaptureStateActive)
+ mutedState.remove(displayMutedFlags);
+ else if (state == WKDisplayCaptureStateMuted)
+ mutedState.add(displayMutedFlags);
+ _page->setMuted(mutedState, [completionHandler = makeBlockPtr(completionHandler)] {
+ completionHandler();
+ });
+}
+
+- (void)_setSystemAudioCaptureState:(WKSystemAudioCaptureState)state completionHandler:(void (^)(void))completionHandler
+{
+ THROW_IF_SUSPENDED;
+ if (!completionHandler)
+ completionHandler = [] { };
+
+ if (state == WKSystemAudioCaptureStateNone) {
+ _page->stopMediaCapture(WebCore::MediaProducerMediaCaptureKind::SystemAudio, [completionHandler = makeBlockPtr(completionHandler)] {
+ completionHandler();
+ });
+ return;
+ }
+ auto mutedState = _page->mutedStateFlags();
+ if (state == WKSystemAudioCaptureStateActive)
+ mutedState.remove(WebCore::MediaProducerMutedState::WindowCaptureIsMuted);
+ else if (state == WKSystemAudioCaptureStateMuted)
+ mutedState.add(WebCore::MediaProducerMutedState::WindowCaptureIsMuted);
+ _page->setMuted(mutedState, [completionHandler = makeBlockPtr(completionHandler)] {
+ completionHandler();
+ });
+}
+
@end
@implementation WKWebView (WKDeprecated)
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h (288893 => 288894)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h 2022-02-01 18:34:18 UTC (rev 288894)
@@ -427,6 +427,65 @@
- (void)_requestResource:(NSURLRequest *)request completionHandler:(void(^)(NSData *, NSURLResponse *, NSError *))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+typedef NS_ENUM(NSInteger, WKDisplayCaptureState) {
+ WKDisplayCaptureStateNone,
+ WKDisplayCaptureStateActive,
+ WKDisplayCaptureStateMuted,
+} WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
+typedef NS_ENUM(NSInteger, WKSystemAudioCaptureState) {
+ WKSystemAudioCaptureStateNone,
+ WKSystemAudioCaptureStateActive,
+ WKSystemAudioCaptureStateMuted,
+} WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
+typedef NS_OPTIONS(NSUInteger, WKDisplayCaptureSurfaces) {
+ WKDisplayCaptureSurfaceNone = 0,
+ WKDisplayCaptureSurfaceScreen = 0x1,
+ WKDisplayCaptureSurfaceWindow = 0x2,
+};
+
+/*! @abstract The type(s) of displays being captured on a web page.
+ @discussion @link WKWebView @/link is key-value observing (KVO) compliant
+ for this property.
+ */
+@property (nonatomic, readonly) WKDisplayCaptureSurfaces _displayCaptureSurfaces WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
+/*! @abstract The state of display capture on a web page.
+ @discussion @link WKWebView @/link is key-value observing (KVO) compliant
+ for this property.
+ */
+@property (nonatomic, readonly) WKDisplayCaptureState _displayCaptureState WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
+/*! @abstract The state of system audio capture on a web page.
+ @discussion @link WKWebView @/link is key-value observing (KVO) compliant
+ for this property.
+ */
+@property (nonatomic, readonly) WKSystemAudioCaptureState _systemAudioCaptureState WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
+/*! @abstract Set display capture state of a WKWebView.
+ @param state State to apply for capture.
+ @param completionHandler A block to invoke after the screen state has been changed.
+ @discussion
+ If value is WKDisplayCaptureStateNone, this will stop all display capture.
+ If value is WKDisplayCaptureStateMuted, all active display captures will become muted.
+ If value is WKDisplayCaptureStateActive, all muted display captures will become active.
+ */
+- (void)_setDisplayCaptureState:(WKDisplayCaptureState)state completionHandler:(void (^)(void))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
+/*! @abstract Set system audio capture state of a WKWebView.
+ @param state State to apply for system audio capture.
+ @param completionHandler A block to invoke after the system audio state has been changed.
+ @discussion
+ If value is WKSystemAudioCaptureStateNone, this will stop any system audio capture.
+ If value is WKSystemAudioCaptureStateMuted, any active system audio capture will become muted.
+ If value is WKSystemAudioCaptureStateActive, any muted system audio capture will become active.
+ @note When system audio capture is active, if screenCaptureState is active, all system audio will be captured.
+ Otherwise, if windowCaptureState is active, only the application whose window being is captured will have its audio captured.
+ If both screenCaptureState and windowCaptureState are None or Muted, no system audio will be captured.
+ */
+- (void)_setSystemAudioCaptureState:(WKSystemAudioCaptureState)state completionHandler:(void (^)(void))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
@end
#if TARGET_OS_IPHONE
Modified: trunk/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp (288893 => 288894)
--- trunk/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -364,7 +364,7 @@
g_object_notify_by_pspec(G_OBJECT(webView), sObjProperties[PROP_MICROPHONE_CAPTURE_STATE]);
return;
}
- if (mediaStateFlags.containsAny(WebCore::MediaProducer::AudioCaptureMask))
+ if (mediaStateFlags.containsAny(WebCore::MediaProducer::MicrophoneCaptureMask))
g_object_notify_by_pspec(G_OBJECT(webView), sObjProperties[PROP_MICROPHONE_CAPTURE_STATE]);
if (mediaStateFlags.containsAny(WebCore::MediaProducer::VideoCaptureMask))
g_object_notify_by_pspec(G_OBJECT(webView), sObjProperties[PROP_CAMERA_CAPTURE_STATE]);
@@ -4868,7 +4868,7 @@
getPage(webView).setCORSDisablingPatterns(WTFMove(allowListVector));
}
-static void webkitWebViewConfigureMediaCapture(WebKitWebView* webView, WebCore::MediaProducerMediaCaptureKind captureKind, WebKitMediaCaptureState captureState, bool isFromDisplayCapture = false)
+static void webkitWebViewConfigureMediaCapture(WebKitWebView* webView, WebCore::MediaProducerMediaCaptureKind captureKind, WebKitMediaCaptureState captureState)
{
auto& page = getPage(webView);
auto mutedState = page.mutedStateFlags();
@@ -4877,13 +4877,15 @@
case WEBKIT_MEDIA_CAPTURE_STATE_NONE:
page.stopMediaCapture(captureKind, [webView, captureKind] {
switch (captureKind) {
- case WebCore::MediaProducerMediaCaptureKind::Audio:
+ case WebCore::MediaProducerMediaCaptureKind::Microphone:
g_object_notify_by_pspec(G_OBJECT(webView), sObjProperties[PROP_MICROPHONE_CAPTURE_STATE]);
break;
- case WebCore::MediaProducerMediaCaptureKind::Video:
+ case WebCore::MediaProducerMediaCaptureKind::Display:
+ case WebCore::MediaProducerMediaCaptureKind::Camera:
g_object_notify_by_pspec(G_OBJECT(webView), sObjProperties[PROP_CAMERA_CAPTURE_STATE]);
break;
- case WebCore::MediaProducerMediaCaptureKind::AudioVideo:
+ case WebCore::MediaProducerMediaCaptureKind::SystemAudio:
+ case WebCore::MediaProducerMediaCaptureKind::EveryKind:
ASSERT_NOT_REACHED();
return;
}
@@ -4891,16 +4893,17 @@
break;
case WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE:
switch (captureKind) {
- case WebCore::MediaProducerMediaCaptureKind::Audio:
+ case WebCore::MediaProducerMediaCaptureKind::Microphone:
mutedState.remove(WebCore::MediaProducer::MutedState::AudioCaptureIsMuted);
break;
- case WebCore::MediaProducerMediaCaptureKind::Video:
- if (isFromDisplayCapture)
- mutedState.remove(WebCore::MediaProducer::MutedState::ScreenCaptureIsMuted);
- else
- mutedState.remove(WebCore::MediaProducer::MutedState::VideoCaptureIsMuted);
+ case WebCore::MediaProducerMediaCaptureKind::Camera:
+ mutedState.remove(WebCore::MediaProducer::MutedState::VideoCaptureIsMuted);
break;
- case WebCore::MediaProducerMediaCaptureKind::AudioVideo:
+ case WebCore::MediaProducerMediaCaptureKind::Display:
+ mutedState.remove(WebCore::MediaProducer::MutedState::ScreenCaptureIsMuted);
+ break;
+ case WebCore::MediaProducerMediaCaptureKind::SystemAudio:
+ case WebCore::MediaProducerMediaCaptureKind::EveryKind:
ASSERT_NOT_REACHED();
return;
}
@@ -4908,16 +4911,17 @@
break;
case WEBKIT_MEDIA_CAPTURE_STATE_MUTED:
switch (captureKind) {
- case WebCore::MediaProducerMediaCaptureKind::Audio:
+ case WebCore::MediaProducerMediaCaptureKind::Microphone:
mutedState.add(WebCore::MediaProducer::MutedState::AudioCaptureIsMuted);
break;
- case WebCore::MediaProducerMediaCaptureKind::Video:
- if (isFromDisplayCapture)
- mutedState.add(WebCore::MediaProducer::MutedState::ScreenCaptureIsMuted);
- else
- mutedState.add(WebCore::MediaProducer::MutedState::VideoCaptureIsMuted);
+ case WebCore::MediaProducerMediaCaptureKind::Camera:
+ mutedState.add(WebCore::MediaProducer::MutedState::VideoCaptureIsMuted);
break;
- case WebCore::MediaProducerMediaCaptureKind::AudioVideo:
+ case WebCore::MediaProducerMediaCaptureKind::Display:
+ mutedState.add(WebCore::MediaProducer::MutedState::ScreenCaptureIsMuted);
+ break;
+ case WebCore::MediaProducerMediaCaptureKind::SystemAudio:
+ case WebCore::MediaProducerMediaCaptureKind::EveryKind:
ASSERT_NOT_REACHED();
return;
}
@@ -4965,7 +4969,7 @@
if (webkit_web_view_get_camera_capture_state(webView) == WEBKIT_MEDIA_CAPTURE_STATE_NONE)
return;
- webkitWebViewConfigureMediaCapture(webView, WebCore::MediaProducerMediaCaptureKind::Video, state);
+ webkitWebViewConfigureMediaCapture(webView, WebCore::MediaProducerMediaCaptureKind::Camera, state);
}
/**
@@ -5007,7 +5011,7 @@
if (webkit_web_view_get_microphone_capture_state(webView) == WEBKIT_MEDIA_CAPTURE_STATE_NONE)
return;
- webkitWebViewConfigureMediaCapture(webView, WebCore::MediaProducerMediaCaptureKind::Audio, state);
+ webkitWebViewConfigureMediaCapture(webView, WebCore::MediaProducerMediaCaptureKind::Microphone, state);
}
/**
@@ -5024,9 +5028,9 @@
WebKitMediaCaptureState webkit_web_view_get_display_capture_state(WebKitWebView* webView)
{
auto state = getPage(webView).reportedMediaState();
- if (state & WebCore::MediaProducer::MediaState::HasActiveDisplayCaptureDevice)
+ if (state & WebCore::MediaProducer::MediaState::HasActiveScreenCaptureDevice)
return WEBKIT_MEDIA_CAPTURE_STATE_ACTIVE;
- if (state & WebCore::MediaProducer::MediaState::HasMutedDisplayCaptureDevice)
+ if (state & WebCore::MediaProducer::MediaState::HasMutedScreenCaptureDevice)
return WEBKIT_MEDIA_CAPTURE_STATE_MUTED;
return WEBKIT_MEDIA_CAPTURE_STATE_NONE;
}
@@ -5049,5 +5053,5 @@
if (webkit_web_view_get_display_capture_state(webView) == WEBKIT_MEDIA_CAPTURE_STATE_NONE)
return;
- webkitWebViewConfigureMediaCapture(webView, WebCore::MediaProducerMediaCaptureKind::Video, state, true);
+ webkitWebViewConfigureMediaCapture(webView, WebCore::MediaProducerMediaCaptureKind::Display, state);
}
Modified: trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.h (288893 => 288894)
--- trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.h 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.h 2022-02-01 18:34:18 UTC (rev 288894)
@@ -84,8 +84,15 @@
void microphoneCaptureWillChange() final;
void cameraCaptureWillChange() final;
+ void displayCaptureWillChange() final;
+ void displayCaptureSurfacesWillChange() final;
+ void systemAudioCaptureWillChange() final;
+
void microphoneCaptureChanged() final;
void cameraCaptureChanged() final;
+ void displayCaptureChanged() final;
+ void displayCaptureSurfacesChanged() final;
+ void systemAudioCaptureChanged() final;
WindowKind windowKind() final;
Modified: trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.mm (288893 => 288894)
--- trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.mm 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.mm 2022-02-01 18:34:18 UTC (rev 288894)
@@ -183,6 +183,21 @@
[m_webView willChangeValueForKey:@"cameraCaptureState"];
}
+void PageClientImplCocoa::displayCaptureWillChange()
+{
+ [m_webView willChangeValueForKey:@"_displayCaptureState"];
+}
+
+void PageClientImplCocoa::displayCaptureSurfacesWillChange()
+{
+ [m_webView willChangeValueForKey:@"_displayCaptureSurfaces"];
+}
+
+void PageClientImplCocoa::systemAudioCaptureWillChange()
+{
+ [m_webView willChangeValueForKey:@"_systemAudioCaptureState"];
+}
+
void PageClientImplCocoa::microphoneCaptureChanged()
{
[m_webView didChangeValueForKey:@"microphoneCaptureState"];
@@ -193,6 +208,21 @@
[m_webView didChangeValueForKey:@"cameraCaptureState"];
}
+void PageClientImplCocoa::displayCaptureChanged()
+{
+ [m_webView didChangeValueForKey:@"_displayCaptureState"];
+}
+
+void PageClientImplCocoa::displayCaptureSurfacesChanged()
+{
+ [m_webView didChangeValueForKey:@"_displayCaptureSurfaces"];
+}
+
+void PageClientImplCocoa::systemAudioCaptureChanged()
+{
+ [m_webView didChangeValueForKey:@"_systemAudioCaptureState"];
+}
+
WindowKind PageClientImplCocoa::windowKind()
{
auto window = [m_webView window];
Modified: trunk/Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp (288893 => 288894)
--- trunk/Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/UIProcess/Cocoa/UserMediaCaptureManagerProxy.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -66,9 +66,12 @@
m_source->addObserver(*this);
switch (m_source->type()) {
case RealtimeMediaSource::Type::Audio:
+ case RealtimeMediaSource::Type::SystemAudio:
m_source->addAudioSampleObserver(*this);
break;
case RealtimeMediaSource::Type::Video:
+ case RealtimeMediaSource::Type::Screen:
+ case RealtimeMediaSource::Type::Window:
m_source->addVideoSampleObserver(*this);
break;
case RealtimeMediaSource::Type::None:
@@ -83,9 +86,12 @@
switch (m_source->type()) {
case RealtimeMediaSource::Type::Audio:
+ case RealtimeMediaSource::Type::SystemAudio:
m_source->removeAudioSampleObserver(*this);
break;
case RealtimeMediaSource::Type::Video:
+ case RealtimeMediaSource::Type::Screen:
+ case RealtimeMediaSource::Type::Window:
m_source->removeVideoSampleObserver(*this);
break;
case RealtimeMediaSource::Type::None:
@@ -288,6 +294,7 @@
case WebCore::CaptureDevice::DeviceType::Window:
sourceOrError = RealtimeMediaSourceCenter::singleton().displayCaptureFactory().createDisplayCaptureSource(device, WTFMove(hashSalt), constraints);
break;
+ case WebCore::CaptureDevice::DeviceType::SystemAudio:
case WebCore::CaptureDevice::DeviceType::Speaker:
case WebCore::CaptureDevice::DeviceType::Unknown:
ASSERT_NOT_REACHED();
Modified: trunk/Source/WebKit/UIProcess/PageClient.h (288893 => 288894)
--- trunk/Source/WebKit/UIProcess/PageClient.h 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/UIProcess/PageClient.h 2022-02-01 18:34:18 UTC (rev 288894)
@@ -565,8 +565,14 @@
virtual void microphoneCaptureWillChange() { }
virtual void cameraCaptureWillChange() { }
+ virtual void displayCaptureWillChange() { }
+ virtual void displayCaptureSurfacesWillChange() { }
+ virtual void systemAudioCaptureWillChange() { }
virtual void microphoneCaptureChanged() { }
virtual void cameraCaptureChanged() { }
+ virtual void displayCaptureChanged() { }
+ virtual void displayCaptureSurfacesChanged() { }
+ virtual void systemAudioCaptureChanged() { }
virtual void videoControlsManagerDidChange() { }
Modified: trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.cpp (288893 => 288894)
--- trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -99,7 +99,7 @@
UserMediaPermissionRequestManagerProxy::~UserMediaPermissionRequestManagerProxy()
{
- m_page.sendWithAsyncReply(Messages::WebPage::StopMediaCapture { MediaProducerMediaCaptureKind::AudioVideo }, [] { });
+ m_page.sendWithAsyncReply(Messages::WebPage::StopMediaCapture { MediaProducerMediaCaptureKind::EveryKind }, [] { });
#if ENABLE(MEDIA_STREAM)
UserMediaProcessManager::singleton().revokeSandboxExtensionsIfNeeded(page().process());
proxies().remove(this);
@@ -133,7 +133,7 @@
{
ALWAYS_LOG(LOGIDENTIFIER);
invalidatePendingRequests();
- m_page.stopMediaCapture(MediaProducerMediaCaptureKind::AudioVideo);
+ m_page.stopMediaCapture(MediaProducerMediaCaptureKind::EveryKind);
}
void UserMediaPermissionRequestManagerProxy::captureDevicesChanged()
Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (288893 => 288894)
--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -9636,13 +9636,25 @@
WEBPAGEPROXY_RELEASE_LOG(WebRTC, "updateReportedMediaCaptureState: from %d to %d", m_reportedMediaCaptureState.toRaw(), activeCaptureState.toRaw());
- bool microphoneCaptureChanged = (m_reportedMediaCaptureState & MediaProducer::AudioCaptureMask) != (activeCaptureState & MediaProducer::AudioCaptureMask);
+ bool microphoneCaptureChanged = (m_reportedMediaCaptureState & MediaProducer::MicrophoneCaptureMask) != (activeCaptureState & MediaProducer::MicrophoneCaptureMask);
bool cameraCaptureChanged = (m_reportedMediaCaptureState & MediaProducer::VideoCaptureMask) != (activeCaptureState & MediaProducer::VideoCaptureMask);
+ bool displayCaptureChanged = (m_reportedMediaCaptureState & MediaProducer::DisplayCaptureMask) != (activeCaptureState & MediaProducer::DisplayCaptureMask);
+ bool systemAudioCaptureChanged = (m_reportedMediaCaptureState & MediaProducer::SystemAudioCaptureMask) != (activeCaptureState & MediaProducer::SystemAudioCaptureMask);
+ auto reportedDisplayCaptureSurfaces = m_reportedMediaCaptureState & (MediaProducer::ScreenCaptureMask | MediaProducer::WindowCaptureMask);
+ auto activeDisplayCaptureSurfaces = activeCaptureState & (MediaProducer::ScreenCaptureMask | MediaProducer::WindowCaptureMask);
+ auto displayCaptureSurfacesChanged = reportedDisplayCaptureSurfaces != activeDisplayCaptureSurfaces;
+
if (microphoneCaptureChanged)
pageClient().microphoneCaptureWillChange();
if (cameraCaptureChanged)
pageClient().cameraCaptureWillChange();
+ if (displayCaptureChanged)
+ pageClient().displayCaptureWillChange();
+ if (displayCaptureSurfacesChanged)
+ pageClient().displayCaptureSurfacesWillChange();
+ if (systemAudioCaptureChanged)
+ pageClient().systemAudioCaptureWillChange();
m_reportedMediaCaptureState = activeCaptureState;
m_uiClient->mediaCaptureStateDidChange(m_mediaState);
@@ -9651,6 +9663,12 @@
pageClient().microphoneCaptureChanged();
if (cameraCaptureChanged)
pageClient().cameraCaptureChanged();
+ if (displayCaptureChanged)
+ pageClient().displayCaptureChanged();
+ if (displayCaptureSurfacesChanged)
+ pageClient().displayCaptureSurfacesChanged();
+ if (systemAudioCaptureChanged)
+ pageClient().systemAudioCaptureChanged();
}
void WebPageProxy::videoControlsManagerDidChange()
Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (288893 => 288894)
--- trunk/Source/WebKit/UIProcess/WebPageProxy.h 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h 2022-02-01 18:34:18 UTC (rev 288894)
@@ -1555,8 +1555,8 @@
enum class CanDelayNotification { No, Yes };
void updatePlayingMediaDidChange(WebCore::MediaProducerMediaStateFlags, CanDelayNotification = CanDelayNotification::No);
- bool isCapturingAudio() const { return m_mediaState.containsAny(WebCore::MediaProducer::AudioCaptureMask); }
- bool isCapturingVideo() const { return m_mediaState.containsAny(WebCore::MediaProducer::VideoCaptureMask); }
+ bool isCapturingAudio() const { return m_mediaState.containsAny(WebCore::MediaProducer::IsCapturingAudioMask); }
+ bool isCapturingVideo() const { return m_mediaState.containsAny(WebCore::MediaProducer::IsCapturingVideoMask); }
bool hasActiveAudioStream() const { return m_mediaState.containsAny(WebCore::MediaProducerMediaState::HasActiveAudioCaptureDevice); }
bool hasActiveVideoStream() const { return m_mediaState.containsAny(WebCore::MediaProducerMediaState::HasActiveVideoCaptureDevice); }
WebCore::MediaProducerMediaStateFlags reportedMediaState() const { return m_reportedMediaCaptureState | (m_mediaState - WebCore::MediaProducer::MediaCaptureMask); }
Modified: trunk/Source/WebKit/WebProcess/WebProcess.cpp (288893 => 288894)
--- trunk/Source/WebKit/WebProcess/WebProcess.cpp 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Source/WebKit/WebProcess/WebProcess.cpp 2022-02-01 18:34:18 UTC (rev 288894)
@@ -1976,7 +1976,7 @@
{
#if ASSERT_ENABLED
bool isCapturingAudio = WTF::anyOf(Document::allDocumentsMap().values(), [](auto* document) {
- return document->mediaState() & MediaProducer::AudioCaptureMask;
+ return document->mediaState() & MediaProducer::MicrophoneCaptureMask;
});
bool isCapturingVideo = WTF::anyOf(Document::allDocumentsMap().values(), [](auto* document) {
return document->mediaState() & MediaProducer::VideoCaptureMask;
Modified: trunk/Tools/ChangeLog (288893 => 288894)
--- trunk/Tools/ChangeLog 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Tools/ChangeLog 2022-02-01 18:34:18 UTC (rev 288894)
@@ -1,3 +1,20 @@
+2022-02-01 Eric Carlson <[email protected]>
+
+ [macOS] Expose screen and window capture state to WKWebView
+ https://bugs.webkit.org/show_bug.cgi?id=235838
+ <rdar://problem/88207924>
+
+ Reviewed by Youenn Fablet.
+
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/xcshareddata/xcschemes/TestWebKitAPI.xcscheme:
+ * TestWebKitAPI/Tests/WebKitCocoa/GetDisplayMediaWindowAndScreen.mm:
+ (-[DisplayCaptureObserver observeValueForKeyPath:ofObject:change:context:]):
+ (-[DisplayCaptureObserver waitForDisplayCaptureState:]):
+ (-[DisplayCaptureObserver waitForDisplayCaptureSurfaces:]):
+ (TestWebKitAPI::TEST):
+ * TestWebKitAPI/glib/TestExpectations.json: Skip /webkit/WebKitWebView/display-usermedia-permission-request
+ until someone has a chance to figure out why it times out.
+
2022-02-01 Sam Sneddon <[email protected]>
Migrate LayoutTestFinder tests from FileSystemMock to pyfakefs
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/GetDisplayMediaWindowAndScreen.mm (288893 => 288894)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/GetDisplayMediaWindowAndScreen.mm 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/GetDisplayMediaWindowAndScreen.mm 2022-02-01 18:34:18 UTC (rev 288894)
@@ -33,9 +33,9 @@
#import "UserMediaCaptureUIDelegate.h"
#import <WebKit/WKPreferencesPrivate.h>
#import <WebKit/WKUIDelegatePrivate.h>
-#import <WebKit/WKWebView.h>
#import <WebKit/WKWebViewConfiguration.h>
#import <WebKit/WKWebViewConfigurationPrivate.h>
+#import <WebKit/WKWebViewPrivate.h>
#import <WebKit/WKWebViewPrivateForTesting.h>
@interface WindowAndScreenCaptureTestView : TestWKWebView
@@ -58,6 +58,62 @@
}
@end
+@interface DisplayCaptureObserver : NSObject {
+ WKDisplayCaptureSurfaces _displayCaptureSurfaces;
+ WKDisplayCaptureState _displayCaptureState;
+}
+
+@property WKDisplayCaptureState displayCaptureState;
+@property WKDisplayCaptureSurfaces displayCaptureSurfaces;
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context;
+- (BOOL)waitForDisplayCaptureState:(WKDisplayCaptureState)expectedState;
+- (BOOL)waitForDisplayCaptureSurfaces:(WKDisplayCaptureSurfaces)expectedState;
+@end
+
+@implementation DisplayCaptureObserver
+
+@synthesize displayCaptureSurfaces = _displayCaptureSurfaces;
+@synthesize displayCaptureState = _displayCaptureState;
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context
+{
+ EXPECT_TRUE([keyPath isEqualToString:NSStringFromSelector(@selector(_displayCaptureSurfaces))] || [keyPath isEqualToString:NSStringFromSelector(@selector(_displayCaptureState))]);
+
+ if ([keyPath isEqualToString:NSStringFromSelector(@selector(_displayCaptureSurfaces))]) {
+ _displayCaptureSurfaces = (WKDisplayCaptureSurfaces)[[change objectForKey:NSKeyValueChangeNewKey] unsignedIntegerValue];
+ return;
+ }
+ _displayCaptureState = (WKDisplayCaptureState)[[change objectForKey:NSKeyValueChangeNewKey] unsignedIntegerValue];
+}
+
+static constexpr unsigned stateChangeQueryMaxCount = 30;
+
+- (BOOL)waitForDisplayCaptureState:(WKDisplayCaptureState)expectedState
+{
+ unsigned tries = 0;
+ do {
+ if (expectedState == _displayCaptureState)
+ return YES;
+ TestWebKitAPI::Util::sleep(0.1);
+ } while (++tries <= stateChangeQueryMaxCount);
+
+ return expectedState == _displayCaptureState;
+}
+
+- (BOOL)waitForDisplayCaptureSurfaces:(WKDisplayCaptureSurfaces)expectedState
+{
+ unsigned tries = 0;
+ do {
+ if (expectedState == _displayCaptureSurfaces)
+ return YES;
+ TestWebKitAPI::Util::sleep(0.1);
+ } while (++tries <= stateChangeQueryMaxCount);
+
+ return expectedState == _displayCaptureSurfaces;
+}
+@end
+
namespace TestWebKitAPI {
TEST(WebKit2, GetDisplayMediaWindowAndScreenPrompt)
@@ -77,7 +133,15 @@
auto webView = adoptNS([[WindowAndScreenCaptureTestView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration.get()]);
[webView setUIDelegate:delegate.get()];
+ auto observer = adoptNS([[DisplayCaptureObserver alloc] init]);
+ [webView addObserver:observer.get() forKeyPath:@"_displayCaptureSurfaces" options:NSKeyValueObservingOptionNew context:nil];
+ [webView addObserver:observer.get() forKeyPath:@"_displayCaptureState" options:NSKeyValueObservingOptionNew context:nil];
+
[webView synchronouslyLoadTestPageNamed:@"getDisplayMedia"];
+ EXPECT_TRUE([webView _displayCaptureState] == WKDisplayCaptureStateNone);
+ EXPECT_TRUE([observer displayCaptureSurfaces] == WKDisplayCaptureSurfaceNone);
+ EXPECT_TRUE([webView _displayCaptureState] == WKDisplayCaptureStateNone);
+ EXPECT_TRUE([observer displayCaptureSurfaces] == WKDisplayCaptureSurfaceNone);
// Check "Don’t Allow"
[delegate setGetDisplayMediaDecision:WKDisplayCapturePermissionDecisionDeny];
@@ -84,6 +148,8 @@
[webView stringByEvaluatingJavaScript:@"promptForCapture({ video : true })"];
[delegate waitUntilPrompted];
EXPECT_TRUE([webView haveStream:NO]);
+ EXPECT_TRUE([webView _displayCaptureState] == WKDisplayCaptureStateNone);
+ EXPECT_TRUE([webView _displayCaptureSurfaces] == WKDisplayCaptureSurfaceNone);
// Check "Allow Screen"
[webView stringByEvaluatingJavaScript:@"stop()"];
@@ -95,9 +161,42 @@
EXPECT_TRUE([webView haveStream:YES]);
auto label = [webView stringByEvaluatingJavaScript:@"stream.getVideoTracks()[0].label"];
EXPECT_WK_STREQ(label, @"Mock screen device 1");
+ EXPECT_TRUE([observer waitForDisplayCaptureState:WKDisplayCaptureStateActive]);
+ EXPECT_TRUE([observer waitForDisplayCaptureSurfaces:WKDisplayCaptureSurfaceScreen]);
+ EXPECT_TRUE([webView _displayCaptureState] == WKDisplayCaptureStateActive);
+ EXPECT_TRUE([observer displayCaptureState] == WKDisplayCaptureStateActive);
+ EXPECT_TRUE([webView _displayCaptureSurfaces] == WKDisplayCaptureSurfaceScreen);
+ EXPECT_TRUE([observer displayCaptureSurfaces] == WKDisplayCaptureSurfaceScreen);
+ // Mute and unmute screen capture
+ __block bool completionCalled = false;
+ [webView _setDisplayCaptureState:WKDisplayCaptureStateMuted completionHandler:^() {
+ completionCalled = true;
+ }];
+ TestWebKitAPI::Util::run(&completionCalled);
+ EXPECT_TRUE([webView _displayCaptureState] == WKDisplayCaptureStateMuted);
+ EXPECT_TRUE([observer displayCaptureState] == WKDisplayCaptureStateMuted);
+ EXPECT_TRUE([webView _displayCaptureSurfaces] == WKDisplayCaptureSurfaceScreen);
+ EXPECT_TRUE([observer displayCaptureSurfaces] == WKDisplayCaptureSurfaceScreen);
+
+ completionCalled = false;
+ [webView _setDisplayCaptureState:WKDisplayCaptureStateActive completionHandler:^() {
+ completionCalled = true;
+ }];
+ TestWebKitAPI::Util::run(&completionCalled);
+ EXPECT_TRUE([webView _displayCaptureState] == WKDisplayCaptureStateActive);
+ EXPECT_TRUE([observer displayCaptureState] == WKDisplayCaptureStateActive);
+ EXPECT_TRUE([webView _displayCaptureSurfaces] == WKDisplayCaptureSurfaceScreen);
+ EXPECT_TRUE([observer displayCaptureSurfaces] == WKDisplayCaptureSurfaceScreen);
+
+ // Stop all capture
+ [webView stringByEvaluatingJavaScript:@"stop()"];
+ EXPECT_TRUE([webView _displayCaptureState] == WKDisplayCaptureStateNone);
+ EXPECT_TRUE([observer displayCaptureSurfaces] == WKDisplayCaptureSurfaceNone);
+ EXPECT_TRUE([webView _displayCaptureState] == WKDisplayCaptureStateNone);
+ EXPECT_TRUE([observer displayCaptureSurfaces] == WKDisplayCaptureSurfaceNone);
+
// Check "Allow Window"
- [webView stringByEvaluatingJavaScript:@"stop()"];
[delegate resetWasPrompted];
[webView _setIndexOfGetDisplayMediaDeviceSelectedForTesting:@0];
[delegate setGetDisplayMediaDecision:WKDisplayCapturePermissionDecisionWindowPrompt];
@@ -106,9 +205,39 @@
EXPECT_TRUE([webView haveStream:YES]);
label = [webView stringByEvaluatingJavaScript:@"stream.getVideoTracks()[0].label"];
EXPECT_WK_STREQ(label, @"Mock window device 1");
+ EXPECT_TRUE([observer waitForDisplayCaptureState:WKDisplayCaptureStateActive]);
+ EXPECT_TRUE([observer waitForDisplayCaptureSurfaces:WKDisplayCaptureSurfaceWindow]);
+ EXPECT_TRUE([webView _displayCaptureState] == WKDisplayCaptureStateActive);
+ EXPECT_TRUE([observer displayCaptureState] == WKDisplayCaptureStateActive);
+ EXPECT_TRUE([webView _displayCaptureSurfaces] == WKDisplayCaptureSurfaceWindow);
+ EXPECT_TRUE([observer displayCaptureSurfaces] == WKDisplayCaptureSurfaceWindow);
+ // Mute and unmute
+ completionCalled = false;
+ [webView _setDisplayCaptureState:WKDisplayCaptureStateMuted completionHandler:^() {
+ completionCalled = true;
+ }];
+ TestWebKitAPI::Util::run(&completionCalled);
+ EXPECT_TRUE([webView _displayCaptureState] == WKDisplayCaptureStateMuted);
+ EXPECT_TRUE([observer displayCaptureState] == WKDisplayCaptureStateMuted);
+ EXPECT_TRUE([webView _displayCaptureSurfaces] == WKDisplayCaptureSurfaceWindow);
+ EXPECT_TRUE([observer displayCaptureSurfaces] == WKDisplayCaptureSurfaceWindow);
+
+ completionCalled = false;
+ [webView _setDisplayCaptureState:WKDisplayCaptureStateActive completionHandler:^() {
+ completionCalled = true;
+ }];
+ TestWebKitAPI::Util::run(&completionCalled);
+ EXPECT_TRUE([webView _displayCaptureState] == WKDisplayCaptureStateActive);
+ EXPECT_TRUE([observer displayCaptureState] == WKDisplayCaptureStateActive);
+ EXPECT_TRUE([webView _displayCaptureSurfaces] == WKDisplayCaptureSurfaceWindow);
+ EXPECT_TRUE([observer displayCaptureSurfaces] == WKDisplayCaptureSurfaceWindow);
+
[webView stringByEvaluatingJavaScript:@"stop()"];
[webView _setIndexOfGetDisplayMediaDeviceSelectedForTesting:nil];
+
+ [webView removeObserver:observer.get() forKeyPath:@"_displayCaptureSurfaces"];
+ [webView removeObserver:observer.get() forKeyPath:@"_displayCaptureState"];
}
} // namespace TestWebKitAPI
Modified: trunk/Tools/TestWebKitAPI/glib/TestExpectations.json (288893 => 288894)
--- trunk/Tools/TestWebKitAPI/glib/TestExpectations.json 2022-02-01 18:29:59 UTC (rev 288893)
+++ trunk/Tools/TestWebKitAPI/glib/TestExpectations.json 2022-02-01 18:34:18 UTC (rev 288894)
@@ -50,7 +50,10 @@
"expected": {"gtk": {"status": ["TIMEOUT"], "bug": "webkit.org/b/205266"}}
},
"/webkit/WebKitWebView/geolocation-permission-requests": {
- "expected": {"gtk": {"status": ["TIMEOUT", "PASS"], "bug": "webkit.org/b/213786"}}
+ "expected": {"gtk": {"status": ["TIMEOUT", "PASS"], "bug": "webkit.org/b/235955"}}
+ },
+ "/webkit/WebKitWebView/display-usermedia-permission-request": {
+ "expected": {"gtk": {"status": ["TIMEOUT", "FAIL"], "bug": "webkit.org/b/235955"}}
}
}
},