Title: [240139] trunk
Revision
240139
Author
wenson_hs...@apple.com
Date
2019-01-17 20:15:24 -0800 (Thu, 17 Jan 2019)

Log Message

[iOS] Content offset jumps erratically when autoscrolling near scroll view content inset areas
https://bugs.webkit.org/show_bug.cgi?id=193494
<rdar://problem/46859627>

Reviewed by Simon Fraser and Tim Horton.

Source/WebCore:

When computing the content offset to scroll to when revealing a given rect in content coordinates, we currently
just use the unobscured content rect. As a result, when scrolling to reveal a rect, we'll clamp the final scroll
position such that only content is visible. For example, when asked to reveal the rect `(0, 0, 1, 1)`, we adjust
the scroll position to be the origin.

However, consider the case where a client (e.g. Mail on iOS) has added a content inset to the web view's scroll
view. If we're asked to reveal a rect that is outside the content area but within a content inset, we will still
end up clamping the scroll position to the unobscured rect. This manifests in a bug where selecting text and
autoscrolling in iOS Mail compose while the scroll view is scrolled all the way to the top to reveal the To/Cc/
Subject fields causes the content offset to jump to the origin, rather than staying at (0, -topContentInset).

To fix this, we teach `RenderLayer::scrollRectToVisible` about content insets that are visible. Rather than use
the content rects as-is, expand to encompass visible content insets as well. This ensures that revealing a
position which is already visible won't cause us to scroll away the content inset area and only show the
unobscured rect.

Tests:  editing/selection/ios/autoscroll-with-top-content-inset.html
        fast/scrolling/ios/scroll-into-view-with-top-content-inset.html

* page/FrameView.cpp:
(WebCore::FrameView::unobscuredContentRectExpandedByContentInsets const):

Introduce a helper method that expands the unobscured content rect to include surrounding content insets.

* page/FrameView.h:
* page/Page.h:
(WebCore::Page::contentInsets const):
(WebCore::Page::setContentInsets):
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::scrollRectToVisible):
(WebCore::RenderLayer::getRectToExpose const):

Source/WebKit:

Adds `contentInsets` to `VisibleContentRectUpdateInfo`. This keeps track of the visible content insets
surrounding the unobscured content rect. See WebCore ChangeLog for more details.

* Shared/VisibleContentRectUpdateInfo.cpp:
(WebKit::VisibleContentRectUpdateInfo::encode const):
(WebKit::VisibleContentRectUpdateInfo::decode):
(WebKit::operator<<):
* Shared/VisibleContentRectUpdateInfo.h:
(WebKit::VisibleContentRectUpdateInfo::VisibleContentRectUpdateInfo):
(WebKit::VisibleContentRectUpdateInfo::contentInsets const):
(WebKit::operator==):
* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _computedObscuredInset]):
(-[WKWebView _computedContentInset]):
(-[WKWebView _computedUnobscuredSafeAreaInset]):

We don't care about source compatibility with iOS 10 and below anymore, so we should change these >= iOS 11
target checks to simply `PLATFORM(IOS)`.

(-[WKWebView _updateVisibleContentRects]):

Compute the visible content insets on all sides of the unobscured content rect. These insets are scaled to
content coordinates.

* UIProcess/ios/WKContentView.h:
* UIProcess/ios/WKContentView.mm:
(floatBoxExtent):

Add a helper to convert `UIEdgeInsets` to `WebCore::FloatBoxExtent`, and use it in a few places below.

(-[WKContentView didUpdateVisibleRect:unobscuredRect:contentInsets:unobscuredRectInScrollViewCoordinates:obscuredInsets:unobscuredSafeAreaInsets:inputViewBounds:scale:minimumScale:inStableState:isChangingObscuredInsetsInteractively:enclosedInScrollableAncestorView:]):
(-[WKContentView didUpdateVisibleRect:unobscuredRect:unobscuredRectInScrollViewCoordinates:obscuredInsets:unobscuredSafeAreaInsets:inputViewBounds:scale:minimumScale:inStableState:isChangingObscuredInsetsInteractively:enclosedInScrollableAncestorView:]): Deleted.
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::updateVisibleContentRects):

Update the Page's content insets.

Tools:

Add a new test option to add a top content inset to the test runner's WKWebView's scroll view, and automatically
scroll to reveal this top content inset area when beginning the test (i.e., scroll to (0, -topContentInset)).

* DumpRenderTree/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptController::contentOffsetX const):
(WTR::UIScriptController::contentOffsetY const):
* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
* TestRunnerShared/UIScriptContext/UIScriptController.cpp:
(WTR::UIScriptController::contentOffsetX const):
(WTR::UIScriptController::contentOffsetY const):
* TestRunnerShared/UIScriptContext/UIScriptController.h:

Also add new UIScriptController methods to ask for the content offset of the platform scroll view.

* WebKitTestRunner/TestController.cpp:
(WTR::updateTestOptionsFromTestHeader):
* WebKitTestRunner/TestOptions.h:
(WTR::TestOptions::hasSameInitializationOptions const):
* WebKitTestRunner/ios/TestControllerIOS.mm:
(WTR::TestController::platformResetStateToConsistentValues):
* WebKitTestRunner/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptController::contentOffsetX const):
(WTR::UIScriptController::contentOffsetY const):

LayoutTests:

* editing/selection/ios/autoscroll-with-top-content-inset-expected.txt: Added.
* editing/selection/ios/autoscroll-with-top-content-inset.html: Added.

Add a new test to verify that moving the selection by autoscrolling near the top content inset area does not
cause the scroll view's content offset to jump.

* fast/scrolling/ios/scroll-into-view-with-top-content-inset-expected.txt: Added.
* fast/scrolling/ios/scroll-into-view-with-top-content-inset.html: Added.

Add a new test to verify that programmatically scrolling an element that's already visible into view does not
scroll away the scroll view's content inset.

* resources/ui-helper.js:
(window.UIHelper.contentOffset):
(window.UIHelper):

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (240138 => 240139)


--- trunk/LayoutTests/ChangeLog	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/LayoutTests/ChangeLog	2019-01-18 04:15:24 UTC (rev 240139)
@@ -1,3 +1,27 @@
+2019-01-17  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [iOS] Content offset jumps erratically when autoscrolling near scroll view content inset areas
+        https://bugs.webkit.org/show_bug.cgi?id=193494
+        <rdar://problem/46859627>
+
+        Reviewed by Simon Fraser and Tim Horton.
+
+        * editing/selection/ios/autoscroll-with-top-content-inset-expected.txt: Added.
+        * editing/selection/ios/autoscroll-with-top-content-inset.html: Added.
+
+        Add a new test to verify that moving the selection by autoscrolling near the top content inset area does not
+        cause the scroll view's content offset to jump.
+
+        * fast/scrolling/ios/scroll-into-view-with-top-content-inset-expected.txt: Added.
+        * fast/scrolling/ios/scroll-into-view-with-top-content-inset.html: Added.
+
+        Add a new test to verify that programmatically scrolling an element that's already visible into view does not
+        scroll away the scroll view's content inset.
+
+        * resources/ui-helper.js:
+        (window.UIHelper.contentOffset):
+        (window.UIHelper):
+
 2019-01-17  John Wilander  <wilan...@apple.com>
 
         Add infrastructure to enable/disable ITP Debug Mode through Preferences

Added: trunk/LayoutTests/editing/selection/ios/autoscroll-with-top-content-inset-expected.txt (0 => 240139)


--- trunk/LayoutTests/editing/selection/ios/autoscroll-with-top-content-inset-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/editing/selection/ios/autoscroll-with-top-content-inset-expected.txt	2019-01-18 04:15:24 UTC (rev 240139)
@@ -0,0 +1,13 @@
+Select me and drag up.
+
+Verifies that triggering autoscroll near the top of a web view with a top content inset does not cause the scroll view's content offset to jump to 0. This test requires WebKitTestRunner. To verify manually, load this page in a web view that has a scroll view top content inset, and scroll such that the full top content inset area is visible. Check that starting a text selection loupe gesture near the top of the top of the document and dragging upwards does not thrash the scroll view's content offset.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS finalContentOffset.x is 0
+PASS verticalMovementDuringDrag < 1 is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/editing/selection/ios/autoscroll-with-top-content-inset.html (0 => 240139)


--- trunk/LayoutTests/editing/selection/ios/autoscroll-with-top-content-inset.html	                        (rev 0)
+++ trunk/LayoutTests/editing/selection/ios/autoscroll-with-top-content-inset.html	2019-01-18 04:15:24 UTC (rev 240139)
@@ -0,0 +1,51 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true, contentInset.top=100 ] -->
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<head>
+    <script src=""
+    <script src=""
+    <script src=""
+    <style>
+    body {
+        margin: 0;
+        box-sizing: border-box;
+        border: red 1px solid;
+    }
+
+    #text {
+        font-size: 30px;
+    }
+
+    #console {
+        overflow: scroll;
+        height: 100px;
+    }
+    </style>
+    <script>
+    jsTestIsAsync = true;
+
+    async function run()
+    {
+        await UIHelper.activateAndWaitForInputSessionAt(110, 40);
+        originalContentOffset = await UIHelper.contentOffset();
+        await longPressAndHoldAtPoint(110, 40);
+        await touchAndDragFromPointToPoint(110, 40, 210, 40);
+        await liftUpAtPoint(210, 40);
+        finalContentOffset = await UIHelper.contentOffset();
+        verticalMovementDuringDrag = Math.abs(finalContentOffset.y - originalContentOffset.y);
+
+        shouldBe("finalContentOffset.x", "0");
+        shouldBeTrue("verticalMovementDuringDrag < 1");
+        finishJSTest();
+    }
+    </script>
+</head>
+<body contenteditable _onload_="run()">
+    <p id="text"><strong>Select me and drag up.</strong></p>
+    <p id="description"></p>
+    <p id="console"></p>
+</body>
+<script>
+    description("Verifies that triggering autoscroll near the top of a web view with a top content inset does not cause the scroll view's content offset to jump to 0. This test requires WebKitTestRunner. To verify manually, load this page in a web view that has a scroll view top content inset, and scroll such that the full top content inset area is visible. Check that starting a text selection loupe gesture near the top of the top of the document and dragging upwards does not thrash the scroll view's content offset.");
+</script>
+</html>

Added: trunk/LayoutTests/fast/scrolling/ios/scroll-into-view-with-top-content-inset-expected.txt (0 => 240139)


--- trunk/LayoutTests/fast/scrolling/ios/scroll-into-view-with-top-content-inset-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/ios/scroll-into-view-with-top-content-inset-expected.txt	2019-01-18 04:15:24 UTC (rev 240139)
@@ -0,0 +1,13 @@
+Verifies that Element.scrollIntoView() does not scroll away the top content inset if the element is already visible. This test requires WebKitTestRunner.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS originalContentOffset.x is 0
+PASS originalContentOffset.y is -100
+PASS finalContentOffset.x is 0
+PASS finalContentOffset.y is -100
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/scrolling/ios/scroll-into-view-with-top-content-inset.html (0 => 240139)


--- trunk/LayoutTests/fast/scrolling/ios/scroll-into-view-with-top-content-inset.html	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/ios/scroll-into-view-with-top-content-inset.html	2019-01-18 04:15:24 UTC (rev 240139)
@@ -0,0 +1,47 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true, contentInset.top=100 ] -->
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<head>
+    <script src=""
+    <script src=""
+    <style>
+    body {
+        margin: 0;
+        border: red 1px solid;
+    }
+
+    #target {
+        position: absolute;
+        width: 4px;
+        height: 4px;
+        top: 0px;
+        left: 0px;
+        background-color: silver;
+    }
+    </style>
+    <script>
+    jsTestIsAsync = true;
+
+    async function run()
+    {
+        originalContentOffset = await UIHelper.contentOffset();
+        target.scrollIntoView({ block: "nearest", inline: "nearest" });
+        finalContentOffset = await UIHelper.contentOffset();
+
+        shouldBe("originalContentOffset.x", "0");
+        shouldBe("originalContentOffset.y", "-100");
+        shouldBe("finalContentOffset.x", "0");
+        shouldBe("finalContentOffset.y", "-100");
+        finishJSTest();
+    }
+    </script>
+</head>
+<body contenteditable _onload_="run()">
+    <div id="target"></div>
+    <p id="description"></p>
+    <p id="console"></p>
+</body>
+<script>
+    description("Verifies that Element.scrollIntoView() does not scroll away the top content inset if the element is already visible. This test requires WebKitTestRunner.");
+</script>
+</html>

Modified: trunk/LayoutTests/resources/ui-helper.js (240138 => 240139)


--- trunk/LayoutTests/resources/ui-helper.js	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/LayoutTests/resources/ui-helper.js	2019-01-18 04:15:24 UTC (rev 240139)
@@ -554,4 +554,16 @@
         const escapedIdentifier = identifier.replace(/`/g, "\\`");
         return new Promise(resolve => testRunner.runUIScript(`uiController.setKeyboardInputModeIdentifier(\`${escapedIdentifier}\`)`, resolve));
     }
+
+    static contentOffset()
+    {
+        if (!this.isIOS())
+            return Promise.resolve();
+
+        const uiScript = "JSON.stringify([uiController.contentOffsetX, uiController.contentOffsetY])";
+        return new Promise(resolve => testRunner.runUIScript(uiScript, result => {
+            const [offsetX, offsetY] = JSON.parse(result)
+            resolve({ x: offsetX, y: offsetY });
+        }));
+    }
 }

Modified: trunk/Source/WebCore/ChangeLog (240138 => 240139)


--- trunk/Source/WebCore/ChangeLog	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Source/WebCore/ChangeLog	2019-01-18 04:15:24 UTC (rev 240139)
@@ -1,3 +1,43 @@
+2019-01-17  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [iOS] Content offset jumps erratically when autoscrolling near scroll view content inset areas
+        https://bugs.webkit.org/show_bug.cgi?id=193494
+        <rdar://problem/46859627>
+
+        Reviewed by Simon Fraser and Tim Horton.
+
+        When computing the content offset to scroll to when revealing a given rect in content coordinates, we currently
+        just use the unobscured content rect. As a result, when scrolling to reveal a rect, we'll clamp the final scroll
+        position such that only content is visible. For example, when asked to reveal the rect `(0, 0, 1, 1)`, we adjust
+        the scroll position to be the origin.
+
+        However, consider the case where a client (e.g. Mail on iOS) has added a content inset to the web view's scroll
+        view. If we're asked to reveal a rect that is outside the content area but within a content inset, we will still
+        end up clamping the scroll position to the unobscured rect. This manifests in a bug where selecting text and
+        autoscrolling in iOS Mail compose while the scroll view is scrolled all the way to the top to reveal the To/Cc/
+        Subject fields causes the content offset to jump to the origin, rather than staying at (0, -topContentInset).
+
+        To fix this, we teach `RenderLayer::scrollRectToVisible` about content insets that are visible. Rather than use
+        the content rects as-is, expand to encompass visible content insets as well. This ensures that revealing a
+        position which is already visible won't cause us to scroll away the content inset area and only show the
+        unobscured rect.
+
+        Tests:  editing/selection/ios/autoscroll-with-top-content-inset.html
+                fast/scrolling/ios/scroll-into-view-with-top-content-inset.html
+
+        * page/FrameView.cpp:
+        (WebCore::FrameView::unobscuredContentRectExpandedByContentInsets const):
+
+        Introduce a helper method that expands the unobscured content rect to include surrounding content insets.
+
+        * page/FrameView.h:
+        * page/Page.h:
+        (WebCore::Page::contentInsets const):
+        (WebCore::Page::setContentInsets):
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::scrollRectToVisible):
+        (WebCore::RenderLayer::getRectToExpose const):
+
 2019-01-17  Truitt Savell  <tsav...@apple.com>
 
         Unreviewed, rolling out r240124.

Modified: trunk/Source/WebCore/page/FrameView.cpp (240138 => 240139)


--- trunk/Source/WebCore/page/FrameView.cpp	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Source/WebCore/page/FrameView.cpp	2019-01-18 04:15:24 UTC (rev 240139)
@@ -2014,6 +2014,14 @@
 #endif
 }
 
+IntRect FrameView::unobscuredContentRectExpandedByContentInsets() const
+{
+    FloatRect unobscuredContentRect = this->unobscuredContentRect();
+    if (auto* page = frame().page())
+        unobscuredContentRect.expand(page->contentInsets());
+    return IntRect(unobscuredContentRect);
+}
+
 bool FrameView::fixedElementsLayoutRelativeToFrame() const
 {
     return frame().settings().fixedElementsLayoutRelativeToFrame();

Modified: trunk/Source/WebCore/page/FrameView.h (240138 => 240139)


--- trunk/Source/WebCore/page/FrameView.h	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Source/WebCore/page/FrameView.h	2019-01-18 04:15:24 UTC (rev 240139)
@@ -326,6 +326,8 @@
     // Static function can be called from another thread.
     WEBCORE_EXPORT static LayoutRect rectForViewportConstrainedObjects(const LayoutRect& visibleContentRect, const LayoutSize& totalContentsSize, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements);
 #endif
+
+    IntRect unobscuredContentRectExpandedByContentInsets() const;
     
     bool fixedElementsLayoutRelativeToFrame() const;
 

Modified: trunk/Source/WebCore/page/Page.h (240138 => 240139)


--- trunk/Source/WebCore/page/Page.h	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Source/WebCore/page/Page.h	2019-01-18 04:15:24 UTC (rev 240139)
@@ -348,6 +348,9 @@
     const FloatBoxExtent& obscuredInsets() const { return m_obscuredInsets; }
     void setObscuredInsets(const FloatBoxExtent& obscuredInsets) { m_obscuredInsets = obscuredInsets; }
 
+    const FloatBoxExtent& contentInsets() const { return m_contentInsets; }
+    void setContentInsets(const FloatBoxExtent& insets) { m_contentInsets = insets; }
+
     const FloatBoxExtent& unobscuredSafeAreaInsets() const { return m_unobscuredSafeAreaInsets; }
     WEBCORE_EXPORT void setUnobscuredSafeAreaInsets(const FloatBoxExtent&);
 
@@ -792,6 +795,7 @@
 
     float m_topContentInset { 0 };
     FloatBoxExtent m_obscuredInsets;
+    FloatBoxExtent m_contentInsets;
     FloatBoxExtent m_unobscuredSafeAreaInsets;
     FloatBoxExtent m_fullscreenInsets;
     Seconds m_fullscreenAutoHideDuration { 0_s };

Modified: trunk/Source/WebCore/rendering/RenderLayer.cpp (240138 => 240139)


--- trunk/Source/WebCore/rendering/RenderLayer.cpp	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Source/WebCore/rendering/RenderLayer.cpp	2019-01-18 04:15:24 UTC (rev 240139)
@@ -2551,7 +2551,7 @@
 #if !PLATFORM(IOS_FAMILY)
             LayoutRect viewRect = frameView.visibleContentRect();
 #else
-            LayoutRect viewRect = frameView.unobscuredContentRect();
+            LayoutRect viewRect = frameView.unobscuredContentRectExpandedByContentInsets();
 #endif
             // Move the target rect into "scrollView contents" coordinates.
             LayoutRect targetRect = absoluteRect;
@@ -2591,7 +2591,7 @@
     }
 }
 
-LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const LayoutRect &exposeRect, bool insideFixed, const ScrollAlignment& alignX, const ScrollAlignment& alignY) const
+LayoutRect RenderLayer::getRectToExpose(const LayoutRect& visibleRect, const LayoutRect& exposeRect, bool insideFixed, const ScrollAlignment& alignX, const ScrollAlignment& alignY) const
 {
     FrameView& frameView = renderer().view().frameView();
     if (renderer().isRenderView() && insideFixed) {

Modified: trunk/Source/WebKit/ChangeLog (240138 => 240139)


--- trunk/Source/WebKit/ChangeLog	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Source/WebKit/ChangeLog	2019-01-18 04:15:24 UTC (rev 240139)
@@ -1,3 +1,48 @@
+2019-01-17  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [iOS] Content offset jumps erratically when autoscrolling near scroll view content inset areas
+        https://bugs.webkit.org/show_bug.cgi?id=193494
+        <rdar://problem/46859627>
+
+        Reviewed by Simon Fraser and Tim Horton.
+
+        Adds `contentInsets` to `VisibleContentRectUpdateInfo`. This keeps track of the visible content insets
+        surrounding the unobscured content rect. See WebCore ChangeLog for more details.
+
+        * Shared/VisibleContentRectUpdateInfo.cpp:
+        (WebKit::VisibleContentRectUpdateInfo::encode const):
+        (WebKit::VisibleContentRectUpdateInfo::decode):
+        (WebKit::operator<<):
+        * Shared/VisibleContentRectUpdateInfo.h:
+        (WebKit::VisibleContentRectUpdateInfo::VisibleContentRectUpdateInfo):
+        (WebKit::VisibleContentRectUpdateInfo::contentInsets const):
+        (WebKit::operator==):
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _computedObscuredInset]):
+        (-[WKWebView _computedContentInset]):
+        (-[WKWebView _computedUnobscuredSafeAreaInset]):
+
+        We don't care about source compatibility with iOS 10 and below anymore, so we should change these >= iOS 11
+        target checks to simply `PLATFORM(IOS)`.
+
+        (-[WKWebView _updateVisibleContentRects]):
+
+        Compute the visible content insets on all sides of the unobscured content rect. These insets are scaled to
+        content coordinates.
+
+        * UIProcess/ios/WKContentView.h:
+        * UIProcess/ios/WKContentView.mm:
+        (floatBoxExtent):
+
+        Add a helper to convert `UIEdgeInsets` to `WebCore::FloatBoxExtent`, and use it in a few places below.
+
+        (-[WKContentView didUpdateVisibleRect:unobscuredRect:contentInsets:unobscuredRectInScrollViewCoordinates:obscuredInsets:unobscuredSafeAreaInsets:inputViewBounds:scale:minimumScale:inStableState:isChangingObscuredInsetsInteractively:enclosedInScrollableAncestorView:]):
+        (-[WKContentView didUpdateVisibleRect:unobscuredRect:unobscuredRectInScrollViewCoordinates:obscuredInsets:unobscuredSafeAreaInsets:inputViewBounds:scale:minimumScale:inStableState:isChangingObscuredInsetsInteractively:enclosedInScrollableAncestorView:]): Deleted.
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::updateVisibleContentRects):
+
+        Update the Page's content insets.
+
 2019-01-17  Truitt Savell  <tsav...@apple.com>
 
         Unreviewed, rolling out r240124.

Modified: trunk/Source/WebKit/Shared/VisibleContentRectUpdateInfo.cpp (240138 => 240139)


--- trunk/Source/WebKit/Shared/VisibleContentRectUpdateInfo.cpp	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Source/WebKit/Shared/VisibleContentRectUpdateInfo.cpp	2019-01-18 04:15:24 UTC (rev 240139)
@@ -37,6 +37,7 @@
 {
     encoder << m_exposedContentRect;
     encoder << m_unobscuredContentRect;
+    encoder << m_contentInsets;
     encoder << m_unobscuredContentRectRespectingInputViewBounds;
     encoder << m_unobscuredRectInScrollViewCoordinates;
     encoder << m_customFixedPositionRect;
@@ -61,6 +62,8 @@
         return false;
     if (!decoder.decode(result.m_unobscuredContentRect))
         return false;
+    if (!decoder.decode(result.m_contentInsets))
+        return false;
     if (!decoder.decode(result.m_unobscuredContentRectRespectingInputViewBounds))
         return false;
     if (!decoder.decode(result.m_unobscuredRectInScrollViewCoordinates))
@@ -114,6 +117,7 @@
 
     ts.dumpProperty("exposedContentRect", info.exposedContentRect());
     ts.dumpProperty("unobscuredContentRect", info.unobscuredContentRect());
+    ts.dumpProperty("contentInsets", info.contentInsets());
     ts.dumpProperty("unobscuredContentRectRespectingInputViewBounds", info.unobscuredContentRectRespectingInputViewBounds());
     ts.dumpProperty("unobscuredRectInScrollViewCoordinates", info.unobscuredRectInScrollViewCoordinates());
     ts.dumpProperty("customFixedPositionRect", info.customFixedPositionRect());

Modified: trunk/Source/WebKit/Shared/VisibleContentRectUpdateInfo.h (240138 => 240139)


--- trunk/Source/WebKit/Shared/VisibleContentRectUpdateInfo.h	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Source/WebKit/Shared/VisibleContentRectUpdateInfo.h	2019-01-18 04:15:24 UTC (rev 240139)
@@ -45,9 +45,10 @@
 public:
     VisibleContentRectUpdateInfo() = default;
 
-    VisibleContentRectUpdateInfo(const WebCore::FloatRect& exposedContentRect, const WebCore::FloatRect& unobscuredContentRect, const WebCore::FloatRect& unobscuredRectInScrollViewCoordinates, const WebCore::FloatRect& unobscuredContentRectRespectingInputViewBounds, const WebCore::FloatRect& customFixedPositionRect, const WebCore::FloatBoxExtent& obscuredInsets, const WebCore::FloatBoxExtent& unobscuredSafeAreaInsets, double scale, bool inStableState, bool isFirstUpdateForNewViewSize, bool isChangingObscuredInsetsInteractively, bool allowShrinkToFit, bool enclosedInScrollableAncestorView, MonotonicTime timestamp, double horizontalVelocity, double verticalVelocity, double scaleChangeRate, uint64_t lastLayerTreeTransactionId)
+    VisibleContentRectUpdateInfo(const WebCore::FloatRect& exposedContentRect, const WebCore::FloatRect& unobscuredContentRect, const WebCore::FloatBoxExtent& contentInsets, const WebCore::FloatRect& unobscuredRectInScrollViewCoordinates, const WebCore::FloatRect& unobscuredContentRectRespectingInputViewBounds, const WebCore::FloatRect& customFixedPositionRect, const WebCore::FloatBoxExtent& obscuredInsets, const WebCore::FloatBoxExtent& unobscuredSafeAreaInsets, double scale, bool inStableState, bool isFirstUpdateForNewViewSize, bool isChangingObscuredInsetsInteractively, bool allowShrinkToFit, bool enclosedInScrollableAncestorView, MonotonicTime timestamp, double horizontalVelocity, double verticalVelocity, double scaleChangeRate, uint64_t lastLayerTreeTransactionId)
         : m_exposedContentRect(exposedContentRect)
         , m_unobscuredContentRect(unobscuredContentRect)
+        , m_contentInsets(contentInsets)
         , m_unobscuredContentRectRespectingInputViewBounds(unobscuredContentRectRespectingInputViewBounds)
         , m_unobscuredRectInScrollViewCoordinates(unobscuredRectInScrollViewCoordinates)
         , m_customFixedPositionRect(customFixedPositionRect)
@@ -69,6 +70,7 @@
 
     const WebCore::FloatRect& exposedContentRect() const { return m_exposedContentRect; }
     const WebCore::FloatRect& unobscuredContentRect() const { return m_unobscuredContentRect; }
+    const WebCore::FloatBoxExtent& contentInsets() const { return m_contentInsets; }
     const WebCore::FloatRect& unobscuredRectInScrollViewCoordinates() const { return m_unobscuredRectInScrollViewCoordinates; }
     const WebCore::FloatRect& unobscuredContentRectRespectingInputViewBounds() const { return m_unobscuredContentRectRespectingInputViewBounds; }
     const WebCore::FloatRect& customFixedPositionRect() const { return m_customFixedPositionRect; }
@@ -97,6 +99,7 @@
 private:
     WebCore::FloatRect m_exposedContentRect;
     WebCore::FloatRect m_unobscuredContentRect;
+    WebCore::FloatBoxExtent m_contentInsets;
     WebCore::FloatRect m_unobscuredContentRectRespectingInputViewBounds;
     WebCore::FloatRect m_unobscuredRectInScrollViewCoordinates;
     WebCore::FloatRect m_customFixedPositionRect; // When visual viewports are enabled, this is the layout viewport.
@@ -121,6 +124,7 @@
     return a.scale() == b.scale()
         && a.exposedContentRect() == b.exposedContentRect()
         && a.unobscuredContentRect() == b.unobscuredContentRect()
+        && a.contentInsets() == b.contentInsets()
         && a.unobscuredContentRectRespectingInputViewBounds() == b.unobscuredContentRectRespectingInputViewBounds()
         && a.customFixedPositionRect() == b.customFixedPositionRect()
         && a.obscuredInsets() == b.obscuredInsets()

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm (240138 => 240139)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm	2019-01-18 04:15:24 UTC (rev 240139)
@@ -1734,7 +1734,7 @@
     if (_haveSetObscuredInsets)
         return _obscuredInsets;
 
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
+#if PLATFORM(IOS)
     if (self._safeAreaShouldAffectObscuredInsets)
         return UIEdgeInsetsAdd(UIEdgeInsetsZero, self._scrollViewSystemContentInset, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
 #endif
@@ -1749,7 +1749,7 @@
 
     UIEdgeInsets insets = [_scrollView contentInset];
 
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
+#if PLATFORM(IOS)
     if (self._safeAreaShouldAffectObscuredInsets)
         insets = UIEdgeInsetsAdd(insets, self._scrollViewSystemContentInset, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
 #endif
@@ -1762,7 +1762,7 @@
     if (_haveSetUnobscuredSafeAreaInsets)
         return _unobscuredSafeAreaInsets;
 
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
+#if PLATFORM(IOS)
     if (!self._safeAreaShouldAffectObscuredInsets)
         return self.safeAreaInsets;
 #endif
@@ -2974,6 +2974,26 @@
     CGRect unobscuredRectInContentCoordinates = _frozenUnobscuredContentRect ? _frozenUnobscuredContentRect.value() : [self convertRect:unobscuredRect toView:_contentView.get()];
     unobscuredRectInContentCoordinates = CGRectIntersection(unobscuredRectInContentCoordinates, [self _contentBoundsExtendedForRubberbandingWithScale:scaleFactor]);
 
+    // The following logic computes the extent to which the bottom, top, left and right content insets are visible.
+    auto scrollViewInsets = [_scrollView contentInset];
+    auto scrollViewBounds = [_scrollView bounds];
+    auto scrollViewContentSize = [_scrollView contentSize];
+    auto scrollViewOriginIncludingInset = UIEdgeInsetsInsetRect(scrollViewBounds, computedContentInsetUnadjustedForKeyboard).origin;
+    auto maximumVerticalScrollExtentWithoutRevealingBottomContentInset = scrollViewContentSize.height - CGRectGetHeight(scrollViewBounds);
+    auto maximumHorizontalScrollExtentWithoutRevealingRightContentInset = scrollViewContentSize.width - CGRectGetWidth(scrollViewBounds);
+    auto contentInsets = UIEdgeInsetsZero;
+    if (scrollViewInsets.left > 0 && scrollViewOriginIncludingInset.x < 0)
+        contentInsets.left = std::min(-scrollViewOriginIncludingInset.x, scrollViewInsets.left) / scaleFactor;
+
+    if (scrollViewInsets.top > 0 && scrollViewOriginIncludingInset.y < 0)
+        contentInsets.top = std::min(-scrollViewOriginIncludingInset.y, scrollViewInsets.top) / scaleFactor;
+
+    if (scrollViewInsets.right > 0 && scrollViewOriginIncludingInset.x > maximumHorizontalScrollExtentWithoutRevealingRightContentInset)
+        contentInsets.right = std::min(scrollViewOriginIncludingInset.x - maximumHorizontalScrollExtentWithoutRevealingRightContentInset, scrollViewInsets.right) / scaleFactor;
+
+    if (scrollViewInsets.bottom > 0 && scrollViewOriginIncludingInset.y > maximumVerticalScrollExtentWithoutRevealingBottomContentInset)
+        contentInsets.bottom = std::min(scrollViewOriginIncludingInset.y - maximumVerticalScrollExtentWithoutRevealingBottomContentInset, scrollViewInsets.bottom) / scaleFactor;
+
 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
     if (inStableState) {
         WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
@@ -2993,6 +3013,7 @@
 
     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
         unobscuredRect:unobscuredRectInContentCoordinates
+        contentInsets:contentInsets
         unobscuredRectInScrollViewCoordinates:unobscuredRect
         obscuredInsets:_obscuredInsets
         unobscuredSafeAreaInsets:[self _computedUnobscuredSafeAreaInset]

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentView.h (240138 => 240139)


--- trunk/Source/WebKit/UIProcess/ios/WKContentView.h	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentView.h	2019-01-18 04:15:24 UTC (rev 240139)
@@ -71,6 +71,7 @@
 
 - (void)didUpdateVisibleRect:(CGRect)visibleRect
     unobscuredRect:(CGRect)unobscuredRect
+    contentInsets:(UIEdgeInsets)contentInsets
     unobscuredRectInScrollViewCoordinates:(CGRect)unobscuredRectInScrollViewCoordinates
     obscuredInsets:(UIEdgeInsets)obscuredInsets
     unobscuredSafeAreaInsets:(UIEdgeInsets)unobscuredSafeAreaInsets

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentView.mm (240138 => 240139)


--- trunk/Source/WebKit/UIProcess/ios/WKContentView.mm	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentView.mm	2019-01-18 04:15:24 UTC (rev 240139)
@@ -365,6 +365,11 @@
     [_textSelectionAssistant deactivateSelection];
 }
 
+static WebCore::FloatBoxExtent floatBoxExtent(UIEdgeInsets insets)
+{
+    return WebCore::FloatBoxExtent(insets.top, insets.right, insets.bottom, insets.left);
+}
+
 - (CGRect)_computeUnobscuredContentRectRespectingInputViewBounds:(CGRect)unobscuredContentRect inputViewBounds:(CGRect)inputViewBounds
 {
     // The input view bounds are in window coordinates, but the unobscured rect is in content coordinates. Account for this by converting input view bounds to content coordinates.
@@ -376,6 +381,7 @@
 
 - (void)didUpdateVisibleRect:(CGRect)visibleContentRect
     unobscuredRect:(CGRect)unobscuredContentRect
+    contentInsets:(UIEdgeInsets)contentInsets
     unobscuredRectInScrollViewCoordinates:(CGRect)unobscuredRectInScrollViewCoordinates
     obscuredInsets:(UIEdgeInsets)obscuredInsets
     unobscuredSafeAreaInsets:(UIEdgeInsets)unobscuredSafeAreaInsets
@@ -404,11 +410,12 @@
     WebKit::VisibleContentRectUpdateInfo visibleContentRectUpdateInfo(
         visibleContentRect,
         unobscuredContentRect,
+        floatBoxExtent(contentInsets),
         unobscuredRectInScrollViewCoordinates,
         unobscuredContentRectRespectingInputViewBounds,
         fixedPositionRectForLayout,
-        WebCore::FloatBoxExtent(obscuredInsets.top, obscuredInsets.right, obscuredInsets.bottom, obscuredInsets.left),
-        WebCore::FloatBoxExtent(unobscuredSafeAreaInsets.top, unobscuredSafeAreaInsets.right, unobscuredSafeAreaInsets.bottom, unobscuredSafeAreaInsets.left),
+        floatBoxExtent(obscuredInsets),
+        floatBoxExtent(unobscuredSafeAreaInsets),
         zoomScale,
         isStableState,
         _sizeChangedSinceLastVisibleContentRectUpdate,

Modified: trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (240138 => 240139)


--- trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm	2019-01-18 04:15:24 UTC (rev 240139)
@@ -3016,6 +3016,7 @@
         viewportConfigurationChanged();
 
     frameView.setUnobscuredContentSize(visibleContentRectUpdateInfo.unobscuredContentRect().size());
+    m_page->setContentInsets(visibleContentRectUpdateInfo.contentInsets());
     m_page->setObscuredInsets(visibleContentRectUpdateInfo.obscuredInsets());
     m_page->setUnobscuredSafeAreaInsets(visibleContentRectUpdateInfo.unobscuredSafeAreaInsets());
     m_page->setEnclosedInScrollableAncestorView(visibleContentRectUpdateInfo.enclosedInScrollableAncestorView());

Modified: trunk/Tools/ChangeLog (240138 => 240139)


--- trunk/Tools/ChangeLog	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Tools/ChangeLog	2019-01-18 04:15:24 UTC (rev 240139)
@@ -1,3 +1,35 @@
+2019-01-17  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [iOS] Content offset jumps erratically when autoscrolling near scroll view content inset areas
+        https://bugs.webkit.org/show_bug.cgi?id=193494
+        <rdar://problem/46859627>
+
+        Reviewed by Simon Fraser and Tim Horton.
+
+        Add a new test option to add a top content inset to the test runner's WKWebView's scroll view, and automatically
+        scroll to reveal this top content inset area when beginning the test (i.e., scroll to (0, -topContentInset)).
+
+        * DumpRenderTree/ios/UIScriptControllerIOS.mm:
+        (WTR::UIScriptController::contentOffsetX const):
+        (WTR::UIScriptController::contentOffsetY const):
+        * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+        * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
+        (WTR::UIScriptController::contentOffsetX const):
+        (WTR::UIScriptController::contentOffsetY const):
+        * TestRunnerShared/UIScriptContext/UIScriptController.h:
+
+        Also add new UIScriptController methods to ask for the content offset of the platform scroll view.
+
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::updateTestOptionsFromTestHeader):
+        * WebKitTestRunner/TestOptions.h:
+        (WTR::TestOptions::hasSameInitializationOptions const):
+        * WebKitTestRunner/ios/TestControllerIOS.mm:
+        (WTR::TestController::platformResetStateToConsistentValues):
+        * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+        (WTR::UIScriptController::contentOffsetX const):
+        (WTR::UIScriptController::contentOffsetY const):
+
 2019-01-17  Truitt Savell  <tsav...@apple.com>
 
         Unreviewed, rolling out r240124.

Modified: trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm (240138 => 240139)


--- trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm	2019-01-18 04:15:24 UTC (rev 240139)
@@ -211,6 +211,16 @@
     return contentOffset;
 }
 
+double UIScriptController::contentOffsetX() const
+{
+    return [gWebScrollView contentOffset].x;
+}
+
+double UIScriptController::contentOffsetY() const
+{
+    return [gWebScrollView contentOffset].y;
+}
+
 void UIScriptController::scrollToOffset(long x, long y)
 {
     [gWebScrollView setContentOffset:contentOffsetBoundedInValidRange(gWebScrollView, CGPointMake(x, y)) animated:YES];

Modified: trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl (240138 => 240139)


--- trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl	2019-01-18 04:15:24 UTC (rev 240139)
@@ -231,6 +231,9 @@
 
     void resignFirstResponder();
 
+    readonly attribute double contentOffsetX;
+    readonly attribute double contentOffsetY;
+
     void scrollToOffset(long x, long y); // Initiate an animated scroll in the UI process.
     attribute object didEndScrollingCallback;
 

Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp (240138 => 240139)


--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp	2019-01-18 04:15:24 UTC (rev 240139)
@@ -336,6 +336,16 @@
     return nullptr;
 }
 
+double UIScriptController::contentOffsetX() const
+{
+    return 0;
+}
+
+double UIScriptController::contentOffsetY() const
+{
+    return 0;
+}
+
 void UIScriptController::scrollToOffset(long x, long y)
 {
 }

Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h (240138 => 240139)


--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h	2019-01-18 04:15:24 UTC (rev 240139)
@@ -113,6 +113,9 @@
     JSObjectRef contentsOfUserInterfaceItem(JSStringRef) const;
     void overridePreference(JSStringRef preference, JSStringRef value);
     
+    double contentOffsetX() const;
+    double contentOffsetY() const;
+
     void scrollToOffset(long x, long y);
 
     void immediateScrollToOffset(long x, long y);

Modified: trunk/Tools/WebKitTestRunner/TestController.cpp (240138 => 240139)


--- trunk/Tools/WebKitTestRunner/TestController.cpp	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Tools/WebKitTestRunner/TestController.cpp	2019-01-18 04:15:24 UTC (rev 240139)
@@ -1266,6 +1266,8 @@
             testOptions.editable = parseBooleanTestHeaderValue(value);
         else if (key == "enableUndoManagerAPI")
             testOptions.enableUndoManagerAPI = parseBooleanTestHeaderValue(value);
+        else if (key == "contentInset.top")
+            testOptions.contentInsetTop = std::stod(value);
         pairStart = pairEnd + 1;
     }
 }

Modified: trunk/Tools/WebKitTestRunner/TestOptions.h (240138 => 240139)


--- trunk/Tools/WebKitTestRunner/TestOptions.h	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Tools/WebKitTestRunner/TestOptions.h	2019-01-18 04:15:24 UTC (rev 240139)
@@ -69,6 +69,8 @@
     bool editable { false };
     bool enableUndoManagerAPI { false };
 
+    double contentInsetTop { 0 };
+
     float deviceScaleFactor { 1 };
     Vector<String> overrideLanguages;
     std::string applicationManifest;
@@ -113,7 +115,8 @@
             || shouldIgnoreMetaViewport != options.shouldIgnoreMetaViewport
             || enableEditableImages != options.enableEditableImages
             || editable != options.editable
-            || enableUndoManagerAPI != options.enableUndoManagerAPI)
+            || enableUndoManagerAPI != options.enableUndoManagerAPI
+            || contentInsetTop != options.contentInsetTop)
             return false;
 
         if (experimentalFeatures != options.experimentalFeatures)

Modified: trunk/Tools/WebKitTestRunner/ios/TestControllerIOS.mm (240138 => 240139)


--- trunk/Tools/WebKitTestRunner/ios/TestControllerIOS.mm	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Tools/WebKitTestRunner/ios/TestControllerIOS.mm	2019-01-18 04:15:24 UTC (rev 240139)
@@ -132,7 +132,8 @@
         UIScrollView *scrollView = webView.scrollView;
         [scrollView _removeAllAnimations:YES];
         [scrollView setZoomScale:1 animated:NO];
-        [scrollView setContentOffset:CGPointZero];
+        scrollView.contentInset = UIEdgeInsetsMake(options.contentInsetTop, 0, 0, 0);
+        scrollView.contentOffset = CGPointMake(0, -options.contentInsetTop);
 
         if (webView.interactingWithFormControl)
             shouldRestoreFirstResponder = [webView resignFirstResponder];

Modified: trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm (240138 => 240139)


--- trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm	2019-01-18 02:11:44 UTC (rev 240138)
+++ trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm	2019-01-18 04:15:24 UTC (rev 240139)
@@ -487,6 +487,18 @@
     return contentOffset;
 }
 
+double UIScriptController::contentOffsetX() const
+{
+    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
+    return webView.scrollView.contentOffset.x;
+}
+
+double UIScriptController::contentOffsetY() const
+{
+    TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
+    return webView.scrollView.contentOffset.y;
+}
+
 void UIScriptController::scrollToOffset(long x, long y)
 {
     TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView();
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to