Title: [249147] trunk
Revision
249147
Author
jer.no...@apple.com
Date
2019-08-27 09:48:21 -0700 (Tue, 27 Aug 2019)

Log Message

Removing fullscreen element in rAF() callback after requestFullscreen() can leave fullscreen in inconsistent state.
https://bugs.webkit.org/show_bug.cgi?id=201101
<rdar://problem/54164587>

Reviewed by Eric Carlson.

Source/WebCore:

Test: fullscreen/full-screen-request-removed-with-raf.html

Add a new state variable, m_pendingFullscreenElement, to track which element is about to
become the fullscreen element, so that when elements are removed or cancelFullscreen() is
called, the state machine inside the fullscreen algorithm can cancel effectively.

* dom/FullscreenManager.cpp:
(WebCore::FullscreenManager::requestFullscreenForElement):
(WebCore::FullscreenManager::cancelFullscreen):
(WebCore::FullscreenManager::exitFullscreen):
(WebCore::FullscreenManager::willEnterFullscreen):
(WebCore::FullscreenManager::willExitFullscreen):
(WebCore::FullscreenManager::didExitFullscreen):
(WebCore::FullscreenManager::adjustFullscreenElementOnNodeRemoval):
(WebCore::FullscreenManager::clear):
(WebCore::FullscreenManager::fullscreenElementRemoved): Deleted.
* dom/FullscreenManager.h:

Source/WebKit:

Add more state to track in which direction the animation is flowing to allow in-process
animations to be cancelled more gracefully.

* UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm:
(-[WKFullScreenWindowController enterFullScreen]):
(-[WKFullScreenWindowController beganEnterFullScreenWithInitialFrame:finalFrame:]):
(-[WKFullScreenWindowController requestExitFullScreen]):
(-[WKFullScreenWindowController exitFullScreen]):
* WebProcess/cocoa/VideoFullscreenManager.h:
(WebKit::VideoFullscreenInterfaceContext::animationState const):
(WebKit::VideoFullscreenInterfaceContext::setAnimationState):
(WebKit::VideoFullscreenInterfaceContext::isAnimating const): Deleted.
(WebKit::VideoFullscreenInterfaceContext::setIsAnimating): Deleted.
* WebProcess/cocoa/VideoFullscreenManager.mm:
(WebKit::VideoFullscreenManager::enterVideoFullscreenForVideoElement):
(WebKit::VideoFullscreenManager::exitVideoFullscreenForVideoElement):
(WebKit::VideoFullscreenManager::didEnterFullscreen):
(WebKit::VideoFullscreenManager::didCleanupFullscreen):

LayoutTests:

* fullscreen/full-screen-request-removed-with-raf-expected.txt: Added.
* fullscreen/full-screen-request-removed-with-raf.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (249146 => 249147)


--- trunk/LayoutTests/ChangeLog	2019-08-27 16:19:37 UTC (rev 249146)
+++ trunk/LayoutTests/ChangeLog	2019-08-27 16:48:21 UTC (rev 249147)
@@ -1,3 +1,14 @@
+2019-08-26  Jer Noble  <jer.no...@apple.com>
+
+        Removing fullscreen element in rAF() callback after requestFullscreen() can leave fullscreen in inconsistent state.
+        https://bugs.webkit.org/show_bug.cgi?id=201101
+        <rdar://problem/54164587>
+
+        Reviewed by Eric Carlson.
+
+        * fullscreen/full-screen-request-removed-with-raf-expected.txt: Added.
+        * fullscreen/full-screen-request-removed-with-raf.html: Added.
+
 2019-08-27  Peng Liu  <peng.l...@apple.com>
 
         webkitpresentationmodechanged is fired twice when exiting picture in picture

Added: trunk/LayoutTests/fullscreen/full-screen-request-removed-with-raf-expected.txt (0 => 249147)


--- trunk/LayoutTests/fullscreen/full-screen-request-removed-with-raf-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fullscreen/full-screen-request-removed-with-raf-expected.txt	2019-08-27 16:48:21 UTC (rev 249147)
@@ -0,0 +1,7 @@
+Tests that fullscreen is not entered if the fullscreen element ancestor is removed with rAF while entering fullscreen.
+
+Removed child element.
+
+SUCCESS
+END OF TEST
+

Added: trunk/LayoutTests/fullscreen/full-screen-request-removed-with-raf.html (0 => 249147)


--- trunk/LayoutTests/fullscreen/full-screen-request-removed-with-raf.html	                        (rev 0)
+++ trunk/LayoutTests/fullscreen/full-screen-request-removed-with-raf.html	2019-08-27 16:48:21 UTC (rev 249147)
@@ -0,0 +1,43 @@
+
+<script src=""
+<script>
+    
+    window.addEventListener('load', (ev) => {
+        var target = document.querySelector('#target');
+
+        document.addEventListener('webkitfullscreenchange', (ev) => { 
+            if (document.webkitCurrentFullScreenElement && !document.webkitCurrentFullScreenElement.ownerDocument)
+                consoleWrite("FAIL: document.webkitCurrentFullScreenElement.ownerDocument is NULL!");
+            else
+                consoleWrite("SUCCESS");
+
+            document.webkitExitFullscreen();
+            endTest();    
+        }, {once: true});
+
+        document.addEventListener('webkitfullscreenerror', (ev) => {
+            consoleWrite("SUCCESS");
+            endTest();    
+        }, {once: true});
+
+        function test()
+        {
+            target.webkitRequestFullScreen();
+            window.requestAnimationFrame(() => {
+                consoleWrite("Removed child element.");
+                target.parentNode.removeChild(target);
+                consoleWrite("");
+            });
+        }  
+
+        runWithKeyDown(test);
+    });
+
+</script>
+
+<p>Tests that fullscreen is not entered if the fullscreen element ancestor is removed with rAF while entering fullscreen.</p>
+
+<div id=ancestor>
+    <span id=target>Fullscreen target!</span>
+</div>
+

Modified: trunk/Source/WebCore/ChangeLog (249146 => 249147)


--- trunk/Source/WebCore/ChangeLog	2019-08-27 16:19:37 UTC (rev 249146)
+++ trunk/Source/WebCore/ChangeLog	2019-08-27 16:48:21 UTC (rev 249147)
@@ -1,3 +1,29 @@
+2019-08-26  Jer Noble  <jer.no...@apple.com>
+
+        Removing fullscreen element in rAF() callback after requestFullscreen() can leave fullscreen in inconsistent state.
+        https://bugs.webkit.org/show_bug.cgi?id=201101
+        <rdar://problem/54164587>
+
+        Reviewed by Eric Carlson.
+
+        Test: fullscreen/full-screen-request-removed-with-raf.html
+
+        Add a new state variable, m_pendingFullscreenElement, to track which element is about to
+        become the fullscreen element, so that when elements are removed or cancelFullscreen() is
+        called, the state machine inside the fullscreen algorithm can cancel effectively.
+
+        * dom/FullscreenManager.cpp:
+        (WebCore::FullscreenManager::requestFullscreenForElement):
+        (WebCore::FullscreenManager::cancelFullscreen):
+        (WebCore::FullscreenManager::exitFullscreen):
+        (WebCore::FullscreenManager::willEnterFullscreen):
+        (WebCore::FullscreenManager::willExitFullscreen):
+        (WebCore::FullscreenManager::didExitFullscreen):
+        (WebCore::FullscreenManager::adjustFullscreenElementOnNodeRemoval):
+        (WebCore::FullscreenManager::clear):
+        (WebCore::FullscreenManager::fullscreenElementRemoved): Deleted.
+        * dom/FullscreenManager.h:
+
 2019-08-27  Peng Liu  <peng.l...@apple.com>
 
         webkitpresentationmodechanged is fired twice when exiting picture in picture

Modified: trunk/Source/WebCore/dom/FullscreenManager.cpp (249146 => 249147)


--- trunk/Source/WebCore/dom/FullscreenManager.cpp	2019-08-27 16:19:37 UTC (rev 249146)
+++ trunk/Source/WebCore/dom/FullscreenManager.cpp	2019-08-27 16:48:21 UTC (rev 249147)
@@ -120,7 +120,16 @@
         }
     }
 
+    m_pendingFullscreenElement = element;
+
     m_fullscreenTaskQueue.enqueueTask([this, element = makeRefPtr(element), checkType, hasKeyboardAccess, failedPreflights] () mutable {
+        // Don't allow fullscreen if it has been cancelled or a different fullscreen element
+        // has requested fullscreen.
+        if (m_pendingFullscreenElement != element) {
+            failedPreflights(WTFMove(element));
+            return;
+        }
+
         // Don't allow fullscreen if document is hidden.
         if (document().hidden()) {
             failedPreflights(WTFMove(element));
@@ -209,9 +218,13 @@
         // 5. Return, and run the remaining steps asynchronously.
         // 6. Optionally, perform some animation.
         m_areKeysEnabledInFullscreen = hasKeyboardAccess;
-        m_fullscreenTaskQueue.enqueueTask([this, element = WTFMove(element)] {
-            if (auto page = this->page())
-                page->chrome().client().enterFullScreenForElement(*element.get());
+        m_fullscreenTaskQueue.enqueueTask([this, element = WTFMove(element), failedPreflights = WTFMove(failedPreflights)] () mutable {
+            auto page = this->page();
+            if (!page || document().hidden() || m_pendingFullscreenElement != element || !element->isConnected()) {
+                failedPreflights(element);
+                return;
+            }
+            page->chrome().client().enterFullScreenForElement(*element.get());
         });
 
         // 7. Optionally, display a message indicating how the user can exit displaying the context object fullscreen.
@@ -225,8 +238,13 @@
     // "To fully exit fullscreen act as if the exitFullscreen() method was invoked on the top-level browsing
     // context's document and subsequently empty that document's fullscreen element stack."
     Document& topDocument = document().topDocument();
-    if (!topDocument.fullscreenManager().fullscreenElement())
+    if (!topDocument.fullscreenManager().fullscreenElement()) {
+        // If there is a pending fullscreen element but no top document fullscreen element,
+        // there is a pending task in enterFullscreen(). Cause it to cancel and fire an error
+        // by clearing the pending fullscreen element.
+        m_pendingFullscreenElement = nullptr;
         return;
+    }
 
     // To achieve that aim, remove all the elements from the top document's stack except for the first before
     // calling webkitExitFullscreen():
@@ -245,8 +263,13 @@
     Document* currentDoc = &document();
 
     // 2. If doc's fullscreen element stack is empty, terminate these steps.
-    if (m_fullscreenElementStack.isEmpty())
+    if (m_fullscreenElementStack.isEmpty()) {
+        // If there is a pending fullscreen element but an empty fullscreen element stack,
+        // there is a pending task in requestFullscreenForElement(). Cause it to cancel and fire an error
+        // by clearing the pending fullscreen element.
+        m_pendingFullscreenElement = nullptr;
         return;
+    }
 
     // 3. Let descendants be all the doc's descendant browsing context's documents with a non-empty fullscreen
     // element stack (if any), ordered so that the child of the doc is last and the document furthest
@@ -298,6 +321,14 @@
         if (!page)
             return;
 
+        // If there is a pending fullscreen element but no fullscreen element
+        // there is a pending task in requestFullscreenForElement(). Cause it to cancel and fire an error
+        // by clearing the pending fullscreen element.
+        if (!fullscreenElement && m_pendingFullscreenElement) {
+            m_pendingFullscreenElement = nullptr;
+            return;
+        }
+
         // Only exit out of full screen window mode if there are no remaining elements in the
         // full screen stack.
         if (!newTop) {
@@ -339,6 +370,13 @@
     if (!page())
         return;
 
+    // If pending fullscreen element is unset or another element's was requested,
+    // issue a cancel fullscreen request to the client
+    if (m_pendingFullscreenElement != &element) {
+        page()->chrome().client().exitFullScreenForElement(&element);
+        return;
+    }
+
     ASSERT(page()->settings().fullScreenEnabled());
 
     unwrapFullscreenRenderer(m_fullscreenRenderer.get(), m_fullscreenElement.get());
@@ -345,6 +383,8 @@
 
     element.willBecomeFullscreenElement();
 
+    ASSERT(&element == m_pendingFullscreenElement);
+    m_pendingFullscreenElement = nullptr;
     m_fullscreenElement = &element;
 
 #if USE(NATIVE_FULLSCREEN_VIDEO)
@@ -385,30 +425,32 @@
 
 void FullscreenManager::willExitFullscreen()
 {
-    if (!m_fullscreenElement)
+    auto fullscreenElement = fullscreenOrPendingElement();
+    if (!fullscreenElement)
         return;
 
     if (!hasLivingRenderTree() || pageCacheState() != Document::NotInPageCache)
         return;
 
-    m_fullscreenElement->willStopBeingFullscreenElement();
+    fullscreenElement->willStopBeingFullscreenElement();
 }
 
 void FullscreenManager::didExitFullscreen()
 {
-    if (!m_fullscreenElement)
+    auto fullscreenElement = fullscreenOrPendingElement();
+    if (!fullscreenElement)
         return;
 
     if (!hasLivingRenderTree() || pageCacheState() != Document::NotInPageCache)
         return;
+    fullscreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
 
-    m_fullscreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
-
     m_areKeysEnabledInFullscreen = false;
 
     unwrapFullscreenRenderer(m_fullscreenRenderer.get(), m_fullscreenElement.get());
 
     m_fullscreenElement = nullptr;
+    m_pendingFullscreenElement = nullptr;
     scheduleFullStyleRebuild();
 
     // When webkitCancelFullscreen is called, we call webkitExitFullscreen on the topDocument(). That
@@ -483,25 +525,22 @@
     }
 }
 
-void FullscreenManager::fullscreenElementRemoved()
-{
-    m_fullscreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
-    cancelFullscreen();
-}
-
 void FullscreenManager::adjustFullscreenElementOnNodeRemoval(Node& node, Document::NodeRemoval nodeRemoval)
 {
-    if (!m_fullscreenElement)
+    auto fullscreenElement = fullscreenOrPendingElement();
+    if (!fullscreenElement)
         return;
 
     bool elementInSubtree = false;
     if (nodeRemoval == Document::NodeRemoval::ChildrenOfNode)
-        elementInSubtree = m_fullscreenElement->isDescendantOf(node);
+        elementInSubtree = fullscreenElement->isDescendantOf(node);
     else
-        elementInSubtree = (m_fullscreenElement == &node) || m_fullscreenElement->isDescendantOf(node);
+        elementInSubtree = (fullscreenElement == &node) || fullscreenElement->isDescendantOf(node);
 
-    if (elementInSubtree)
-        fullscreenElementRemoved();
+    if (elementInSubtree) {
+        fullscreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
+        cancelFullscreen();
+    }
 }
 
 bool FullscreenManager::isAnimatingFullscreen() const
@@ -541,6 +580,7 @@
 void FullscreenManager::clear()
 {
     m_fullscreenElement = nullptr;
+    m_pendingFullscreenElement = nullptr;
     m_fullscreenElementStack.clear();
 }
 

Modified: trunk/Source/WebCore/dom/FullscreenManager.h (249146 => 249147)


--- trunk/Source/WebCore/dom/FullscreenManager.h	2019-08-27 16:19:37 UTC (rev 249146)
+++ trunk/Source/WebCore/dom/FullscreenManager.h	2019-08-27 16:48:21 UTC (rev 249147)
@@ -109,6 +109,9 @@
 private:
     Document& m_document;
 
+    RefPtr<Element> fullscreenOrPendingElement() const { return m_fullscreenElement ? m_fullscreenElement : m_pendingFullscreenElement; }
+
+    RefPtr<Element> m_pendingFullscreenElement;
     RefPtr<Element> m_fullscreenElement;
     Vector<RefPtr<Element>> m_fullscreenElementStack;
     WeakPtr<RenderFullScreen> m_fullscreenRenderer { nullptr };

Modified: trunk/Source/WebKit/ChangeLog (249146 => 249147)


--- trunk/Source/WebKit/ChangeLog	2019-08-27 16:19:37 UTC (rev 249146)
+++ trunk/Source/WebKit/ChangeLog	2019-08-27 16:48:21 UTC (rev 249147)
@@ -1,3 +1,30 @@
+2019-08-26  Jer Noble  <jer.no...@apple.com>
+
+        Removing fullscreen element in rAF() callback after requestFullscreen() can leave fullscreen in inconsistent state.
+        https://bugs.webkit.org/show_bug.cgi?id=201101
+        <rdar://problem/54164587>
+
+        Reviewed by Eric Carlson.
+
+        Add more state to track in which direction the animation is flowing to allow in-process
+        animations to be cancelled more gracefully.
+
+        * UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm:
+        (-[WKFullScreenWindowController enterFullScreen]):
+        (-[WKFullScreenWindowController beganEnterFullScreenWithInitialFrame:finalFrame:]):
+        (-[WKFullScreenWindowController requestExitFullScreen]):
+        (-[WKFullScreenWindowController exitFullScreen]):
+        * WebProcess/cocoa/VideoFullscreenManager.h:
+        (WebKit::VideoFullscreenInterfaceContext::animationState const):
+        (WebKit::VideoFullscreenInterfaceContext::setAnimationState):
+        (WebKit::VideoFullscreenInterfaceContext::isAnimating const): Deleted.
+        (WebKit::VideoFullscreenInterfaceContext::setIsAnimating): Deleted.
+        * WebProcess/cocoa/VideoFullscreenManager.mm:
+        (WebKit::VideoFullscreenManager::enterVideoFullscreenForVideoElement):
+        (WebKit::VideoFullscreenManager::exitVideoFullscreenForVideoElement):
+        (WebKit::VideoFullscreenManager::didEnterFullscreen):
+        (WebKit::VideoFullscreenManager::didCleanupFullscreen):
+
 2019-08-27  Carlos Garcia Campos  <cgar...@igalia.com>
 
         Do not clear the pending api request when there's no navigation ID

Modified: trunk/Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm (249146 => 249147)


--- trunk/Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm	2019-08-27 16:19:37 UTC (rev 249146)
+++ trunk/Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm	2019-08-27 16:48:21 UTC (rev 249147)
@@ -447,6 +447,7 @@
     RetainPtr<NSString> _EVOrganizationName;
     BOOL _EVOrganizationNameIsValid;
     BOOL _inInteractiveDismiss;
+    BOOL _exitRequested;
 
     RetainPtr<id> _notificationListener;
 }
@@ -597,6 +598,13 @@
 
         _repaintCallback = WebKit::VoidCallback::create([protectedSelf = retainPtr(self), self](WebKit::CallbackBase::Error) {
             _repaintCallback = nullptr;
+
+            if (_exitRequested) {
+                _exitRequested = NO;
+                [self _exitFullscreenImmediately];
+                return;
+            }
+
             if (auto* manager = [protectedSelf _manager]) {
                 manager->willEnterFullScreen();
                 return;
@@ -640,6 +648,12 @@
     [_rootViewController presentViewController:_fullscreenViewController.get() animated:YES completion:^{
         _fullScreenState = WebKit::InFullScreen;
 
+        if (_exitRequested) {
+            _exitRequested = NO;
+            [self _exitFullscreenImmediately];
+            return;
+        }
+
         auto* page = [self._webView _page];
         auto* manager = self._manager;
         if (page && manager) {
@@ -657,6 +671,11 @@
 
 - (void)requestExitFullScreen
 {
+    if (_fullScreenState != WebKit::InFullScreen) {
+        _exitRequested = YES;
+        return;
+    }
+
     if (auto* manager = self._manager) {
         manager->requestExitFullScreen();
         return;
@@ -668,8 +687,10 @@
 
 - (void)exitFullScreen
 {
-    if (!self.isFullScreen)
+    if (_fullScreenState < WebKit::InFullScreen) {
+        _exitRequested = YES;
         return;
+    }
     _fullScreenState = WebKit::WaitingToExitFullScreen;
 
     if (auto* manager = self._manager) {

Modified: trunk/Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.h (249146 => 249147)


--- trunk/Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.h	2019-08-27 16:19:37 UTC (rev 249146)
+++ trunk/Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.h	2019-08-27 16:48:21 UTC (rev 249147)
@@ -73,8 +73,9 @@
     LayerHostingContext* layerHostingContext() { return m_layerHostingContext.get(); }
     void setLayerHostingContext(std::unique_ptr<LayerHostingContext>&&);
 
-    bool isAnimating() const { return m_isAnimating; }
-    void setIsAnimating(bool flag) { m_isAnimating = flag; }
+    enum class AnimationType { None, IntoFullscreen, FromFullscreen };
+    AnimationType animationState() const { return m_animationType; }
+    void setAnimationState(AnimationType flag) { m_animationType = flag; }
 
     bool targetIsFullscreen() const { return m_targetIsFullscreen; }
     void setTargetIsFullscreen(bool flag) { m_targetIsFullscreen = flag; }
@@ -98,7 +99,7 @@
     VideoFullscreenManager* m_manager;
     uint64_t m_contextId;
     std::unique_ptr<LayerHostingContext> m_layerHostingContext;
-    bool m_isAnimating { false };
+    AnimationType m_animationType { false };
     bool m_targetIsFullscreen { false };
     WebCore::HTMLMediaElementEnums::VideoFullscreenMode m_fullscreenMode { WebCore::HTMLMediaElementEnums::VideoFullscreenModeNone };
     bool m_fullscreenStandby { false };

Modified: trunk/Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.mm (249146 => 249147)


--- trunk/Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.mm	2019-08-27 16:19:37 UTC (rev 249146)
+++ trunk/Source/WebKit/WebProcess/cocoa/VideoFullscreenManager.mm	2019-08-27 16:48:21 UTC (rev 249147)
@@ -260,9 +260,9 @@
     if (oldMode == HTMLMediaElementEnums::VideoFullscreenModeNone && mode != HTMLMediaElementEnums::VideoFullscreenModeNone)
         model->setVideoLayerFrame(videoLayerFrame);
 
-    if (interface->isAnimating())
+    if (interface->animationState() != VideoFullscreenInterfaceContext::AnimationType::None)
         return;
-    interface->setIsAnimating(true);
+    interface->setAnimationState(VideoFullscreenInterfaceContext::AnimationType::IntoFullscreen);
 
     bool allowsPictureInPicture = videoElement.webkitSupportsPresentationMode(HTMLVideoElement::VideoPresentationMode::PictureInPicture);
 
@@ -296,10 +296,9 @@
 
     interface.setTargetIsFullscreen(false);
 
-    if (interface.isAnimating())
+    if (interface.animationState() == VideoFullscreenInterfaceContext::AnimationType::FromFullscreen)
         return;
-
-    interface.setIsAnimating(true);
+    interface.setAnimationState(VideoFullscreenInterfaceContext::AnimationType::FromFullscreen);
     m_page->send(Messages::VideoFullscreenManagerProxy::ExitFullscreen(contextId, inlineVideoFrame(videoElement)));
 }
 
@@ -438,7 +437,7 @@
 
     auto [model, interface] = ensureModelAndInterface(contextId);
 
-    interface->setIsAnimating(false);
+    interface->setAnimationState(VideoFullscreenInterfaceContext::AnimationType::None);
     interface->setIsFullscreen(false);
 
     RefPtr<HTMLVideoElement> videoElement = model->videoElement();
@@ -501,7 +500,7 @@
         interface->setLayerHostingContext(nullptr);
     }
 
-    interface->setIsAnimating(false);
+    interface->setAnimationState(VideoFullscreenInterfaceContext::AnimationType::None);
     interface->setIsFullscreen(false);
     HTMLMediaElementEnums::VideoFullscreenMode mode = interface->fullscreenMode();
     bool standby = interface->fullscreenStandby();
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to