WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
scrollend-programmatic-for-review
scrollend-programmatic-007-review.diff (text/plain), 59.56 KB, created by
cathiechen
on 2020-04-09 05:25:54 PDT
(
hide
)
Description:
scrollend-programmatic-for-review
Filename:
MIME Type:
Creator:
cathiechen
Created:
2020-04-09 05:25:54 PDT
Size:
59.56 KB
patch
obsolete
>diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 36f124f85e..913645fa0e 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,15 @@ >+2020-04-08 Cathie Chen <cathiechen@igalia.com> >+ >+ Add support for scrollend event of programmatic scrolling >+ https://bugs.webkit.org/show_bug.cgi?id=209134 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * platform/ios/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView-expected.txt: Added. >+ Test fails on iOS because the FrameView scroll position is no clamped, see bug: 179735. >+ * platform/mac-wk1/TestExpectations: Test fails on mac-wk1 because the FrameView smooth scrolling is not functional. See bug: 191357. >+ * TestExpectations: Enable the test for programmatic scrolling scrollend event. >+ > 2020-04-04 Peng Liu <peng.liu6@apple.com> > > REGRESSION (r259095): ASSERTION FAILED: m_videoFullscreenMode != VideoFullscreenModeNone seen with TestWebKitAPI.WebKitLegacy.AudioSessionCategoryIOS >diff --git a/LayoutTests/TestExpectations b/LayoutTests/TestExpectations >index eb16933b8f..9128ab4d8a 100644 >--- a/LayoutTests/TestExpectations >+++ b/LayoutTests/TestExpectations >@@ -164,7 +164,15 @@ http/tests/in-app-browser-privacy/ [ Skip ] > fast/harness/uiscriptcontroller [ Skip ] > > # Only iOS has an implementation of UIScriptController::sendEventStream(). >-imported/w3c/web-platform-tests/dom/events/scrolling [ Skip ] >+imported/w3c/web-platform-tests/dom/events/scrolling/overscroll-event-fired-to-document.html [ Skip ] >+imported/w3c/web-platform-tests/dom/events/scrolling/overscroll-event-fired-to-element-with-overscroll-behavior.html [ Skip ] >+imported/w3c/web-platform-tests/dom/events/scrolling/overscroll-event-fired-to-scrolled-element.html [ Skip ] >+imported/w3c/web-platform-tests/dom/events/scrolling/overscroll-event-fired-to-window.html [ Skip ] >+imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-after-snap.html [ Skip ] >+imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-to-document.html [ Skip ] >+imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-to-element-with-overscroll-behavior.html [ Skip ] >+imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-to-scrolled-element.html [ Skip ] >+imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-to-window.html [ Skip ] > > # This test only makes sense on Mac > fast/attachment/attachment-subtitle-resize.html [ Skip ] >diff --git a/LayoutTests/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-programmatic-scroll-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-programmatic-scroll-expected.txt >new file mode 100644 >index 0000000000..966c9f1abb >--- /dev/null >+++ b/LayoutTests/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-programmatic-scroll-expected.txt >@@ -0,0 +1,4 @@ >+ >+PASS Tests scrollend event for calling scroll functions. >+PASS Tests scrollend event for changing scroll attributes. >+ >diff --git a/LayoutTests/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-programmatic-scroll.html b/LayoutTests/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-programmatic-scroll.html >new file mode 100644 >index 0000000000..43fb7acddf >--- /dev/null >+++ b/LayoutTests/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-programmatic-scroll.html >@@ -0,0 +1,135 @@ >+<!DOCTYPE HTML><!-- webkit-test-runner [ experimental:OverscrollEventsEnabled=true experimental:CSSOMViewSmoothScrollingEnabled=true ] --> >+<meta name="timeout" content="long"> >+<script src="/resources/testharness.js"></script> >+<script src="/resources/testharnessreport.js"></script> >+<script src="/resources/testdriver.js"></script> >+<script src="/resources/testdriver-actions.js"></script> >+<script src="/resources/testdriver-vendor.js"></script> >+<script src="scroll_support.js"></script> >+<style> >+ html { >+ height: 3000px; >+ width: 3000px; >+ } >+ >+ #targetDiv { >+ width: 200px; >+ height: 200px; >+ overflow: scroll; >+ } >+ >+ #innerDiv { >+ width: 400px; >+ height: 400px; >+ } >+</style> >+ >+<body style="margin:0" onload=runTest()> >+ <div id="targetDiv"> >+ <div id="innerDiv"> >+ </div> >+ </div> >+</body> >+<script> >+ var element_scrollend_arrived = false; >+ var document_scrollend_arrived = false; >+ >+ function onElementScrollEnd(event) { >+ assert_false(event.cancelable); >+ assert_false(event.bubbles); >+ element_scrollend_arrived = true; >+ } >+ >+ function onDocumentScrollEnd(event) { >+ assert_false(event.cancelable); >+ // scrollend events are bubbled when the target node is document. >+ assert_true(event.bubbles); >+ document_scrollend_arrived = true; >+ } >+ >+ function callScrollFunction([scrollTarget, scrollFunction, args]) { >+ scrollTarget[scrollFunction](args); >+ } >+ >+ function runTest() { >+ let root_element = document.scrollingElement; >+ let target_div = document.getElementById("targetDiv"); >+ >+ promise_test(async (t) => { >+ await waitForCompositorCommit(); >+ target_div.addEventListener("scrollend", onElementScrollEnd); >+ document.addEventListener("scrollend", onDocumentScrollEnd); >+ >+ let test_cases = [ >+ [target_div, 200, 200, [target_div, "scrollTo", { top: 200, left: 200, behavior: "auto" }]], >+ [target_div, 0, 0, [target_div, "scrollTo", { top: 0, left: 0, behavior: "smooth" }]], >+ [root_element, 200, 200, [root_element, "scrollTo", { top: 200, left: 200, behavior: "auto" }]], >+ [root_element, 0, 0, [root_element, "scrollTo", { top: 0, left: 0, behavior: "smooth" }]], >+ [target_div, 200, 200, [target_div, "scrollBy", { top: 200, left: 200, behavior: "auto" }]], >+ [target_div, 0, 0, [target_div, "scrollBy", { top: -200, left: -200, behavior: "smooth" }]], >+ [root_element, 200, 200, [root_element, "scrollBy", { top: 200, left: 200, behavior: "auto" }]], >+ [root_element, 0, 0, [root_element, "scrollBy", { top: -200, left: -200, behavior: "smooth" }]] >+ ]; >+ >+ for (i = 0; i < test_cases.length; i++) { >+ let t = test_cases[i]; >+ let target = t[0]; >+ let expected_x = t[1]; >+ let expected_y = t[2]; >+ let scroll_datas = t[3]; >+ >+ callScrollFunction(scroll_datas); >+ await waitFor(() => { return element_scrollend_arrived || document_scrollend_arrived; }, target.tagName + "." + scroll_datas[1] + " did not receive scrollend event."); >+ if (target == root_element) >+ assert_false(element_scrollend_arrived); >+ else >+ assert_false(document_scrollend_arrived); >+ assert_equals(target.scrollLeft, expected_x, target.tagName + "." + scroll_datas[1] + " scrollLeft"); >+ assert_equals(target.scrollTop, expected_y, target.tagName + "." + scroll_datas[1] + " scrollTop"); >+ >+ element_scrollend_arrived = false; >+ document_scrollend_arrived = false; >+ } >+ }, "Tests scrollend event for calling scroll functions."); >+ >+ promise_test(async (t) => { >+ await waitForCompositorCommit(); >+ >+ let test_cases = [ >+ [target_div, "scrollTop"], >+ [target_div, "scrollLeft"], >+ [root_element, "scrollTop"], >+ [root_element, "scrollLeft"] >+ ]; >+ for (i = 0; i < test_cases.length; i++) { >+ let t = test_cases[i]; >+ let target = t[0]; >+ let attribute = t[1]; >+ let position = 200; >+ >+ target.style.scrollBehavior = "smooth"; >+ target[attribute] = position; >+ await waitFor(() => { return element_scrollend_arrived || document_scrollend_arrived; }, target.tagName + "." + attribute + " did not receive scrollend event."); >+ if (target == root_element) >+ assert_false(element_scrollend_arrived); >+ else >+ assert_false(document_scrollend_arrived); >+ assert_equals(target[attribute], position, target.tagName + "." + attribute + " "); >+ element_scrollend_arrived = false; >+ document_scrollend_arrived = false; >+ >+ await waitForCompositorCommit(); >+ target.style.scrollBehavior = "auto"; >+ target[attribute] = 0; >+ await waitFor(() => { return element_scrollend_arrived || document_scrollend_arrived; }, target.tagName + "." + attribute + " did not receive scrollend event."); >+ if (target == root_element) >+ assert_false(element_scrollend_arrived); >+ else >+ assert_false(document_scrollend_arrived); >+ assert_equals(target[attribute], 0, target.tagName + "." + attribute + " "); >+ element_scrollend_arrived = false; >+ document_scrollend_arrived = false; >+ } >+ }, "Tests scrollend event for changing scroll attributes."); >+ } >+</script> >\ No newline at end of file >diff --git a/LayoutTests/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView-expected.txt >new file mode 100644 >index 0000000000..bec711fde3 >--- /dev/null >+++ b/LayoutTests/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView-expected.txt >@@ -0,0 +1,4 @@ >+ >+PASS Tests scrollend event for scrollIntoView. >+PASS Tests scrollend event for nested scrollIntoView. >+ >diff --git a/LayoutTests/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView.html b/LayoutTests/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView.html >new file mode 100644 >index 0000000000..edd38c0f2e >--- /dev/null >+++ b/LayoutTests/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView.html >@@ -0,0 +1,124 @@ >+<!DOCTYPE HTML><!-- webkit-test-runner [ experimental:OverscrollEventsEnabled=true experimental:CSSOMViewSmoothScrollingEnabled=true ] --> >+<meta name="timeout" content="long"> >+<script src="/resources/testharness.js"></script> >+<script src="/resources/testharnessreport.js"></script> >+<script src="/resources/testdriver.js"></script> >+<script src="/resources/testdriver-actions.js"></script> >+<script src="/resources/testdriver-vendor.js"></script> >+<script src="scroll_support.js"></script> >+<style> >+ html { >+ height: 3000px; >+ width: 3000px; >+ } >+ >+ #targetDiv { >+ width: 200px; >+ height: 200px; >+ overflow: scroll; >+ } >+ >+ #innerDiv { >+ width: 400px; >+ height: 400px; >+ } >+</style> >+ >+<body style="margin:0" onload=runTest()> >+ <div id="targetDiv"> >+ <div id="innerDiv"> >+ </div> >+ </div> >+</body> >+<script> >+ var element_scrollend_arrived = false; >+ var document_scrollend_arrived = false; >+ >+ function onElementScrollEnd(event) { >+ assert_false(event.cancelable); >+ assert_false(event.bubbles); >+ element_scrollend_arrived = true; >+ } >+ >+ function onDocumentScrollEnd(event) { >+ assert_false(event.cancelable); >+ // scrollend events are bubbled when the target node is document. >+ assert_true(event.bubbles); >+ document_scrollend_arrived = true; >+ } >+ >+ function callScrollFunction([scrollTarget, scrollFunction, args]) { >+ scrollTarget[scrollFunction](args); >+ } >+ >+ function runTest() { >+ let root_element = document.scrollingElement; >+ let target_div = document.getElementById("targetDiv"); >+ let inner_div = document.getElementById("innerDiv"); >+ >+ // Get expected position for root_element scrollIntoView. >+ root_element.scrollTo(10000, 10000); >+ let max_root_x = root_element.scrollLeft; >+ let max_root_y = root_element.scrollTop; >+ root_element.scrollTo(0, 0); >+ >+ target_div.scrollTo(10000, 10000); >+ let max_element_x = target_div.scrollLeft; >+ let max_element_y = target_div.scrollTop; >+ target_div.scrollTo(0, 0); >+ >+ promise_test(async (t) => { >+ await waitForCompositorCommit(); >+ target_div.addEventListener("scrollend", onElementScrollEnd); >+ document.addEventListener("scrollend", onDocumentScrollEnd); >+ >+ let test_cases = [ >+ [target_div, max_element_x, max_element_y, [inner_div, "scrollIntoView", { inline: "end", block: "end", behavior: "auto" }]], >+ [target_div, 0, 0, [inner_div, "scrollIntoView", { inline: "start", block: "start", behavior: "smooth" }]], >+ [root_element, max_root_x, max_root_y, [root_element, "scrollIntoView", { inline: "end", block: "end", behavior: "smooth" }]], >+ [root_element, 0, 0, [root_element, "scrollIntoView", { inline: "start", block: "start", behavior: "smooth" }]] >+ ]; >+ >+ for (i = 0; i < test_cases.length; i++) { >+ let t = test_cases[i]; >+ let target = t[0]; >+ let expected_x = t[1]; >+ let expected_y = t[2]; >+ let scroll_datas = t[3]; >+ >+ callScrollFunction(scroll_datas); >+ await waitFor(() => { return element_scrollend_arrived || document_scrollend_arrived; }, target.tagName + ".scrollIntoView did not receive scrollend event."); >+ if (target == root_element) >+ assert_false(element_scrollend_arrived); >+ else >+ assert_false(document_scrollend_arrived); >+ assert_equals(target.scrollLeft, expected_x, target.tagName + ".scrollIntoView scrollLeft"); >+ assert_equals(target.scrollTop, expected_y, target.tagName + ".scrollIntoView scrollTop"); >+ >+ element_scrollend_arrived = false; >+ document_scrollend_arrived = false; >+ } >+ }, "Tests scrollend event for scrollIntoView."); >+ >+ promise_test(async (t) => { >+ document.body.removeChild(target_div); >+ let out_div = document.createElement("div"); >+ out_div.style = "width: 100px; height:100px; overflow:scroll; scroll-behavior:smooth;"; >+ out_div.appendChild(target_div); >+ document.body.appendChild(out_div); >+ await waitForCompositorCommit(); >+ >+ element_scrollend_arrived = false; >+ document_scrollend_arrived = false; >+ inner_div.scrollIntoView({ inline: "end", block: "end", behavior: "auto" }); >+ await waitFor(() => { return element_scrollend_arrived || document_scrollend_arrived; }, "Nested scrollIntoView did not receive scrollend event."); >+ assert_equals(root_element.scrollLeft, 0, "Nested scrollIntoView root_element scrollLeft"); >+ assert_equals(root_element.scrollTop, 0, "Nested scrollIntoView root_element scrollTop"); >+ assert_equals(out_div.scrollLeft, 100, "Nested scrollIntoView out_div scrollLeft"); >+ assert_equals(out_div.scrollTop, 100, "Nested scrollIntoView out_div scrollTop"); >+ assert_equals(target_div.scrollLeft, max_element_x, "Nested scrollIntoView target_div scrollLeft"); >+ assert_equals(target_div.scrollTop, max_element_y, "Nested scrollIntoView target_div scrollTop"); >+ assert_false(document_scrollend_arrived); >+ }, "Tests scrollend event for nested scrollIntoView."); >+ } >+</script> >\ No newline at end of file >diff --git a/LayoutTests/platform/ios/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView-expected.txt b/LayoutTests/platform/ios/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView-expected.txt >new file mode 100644 >index 0000000000..4abaa6a231 >--- /dev/null >+++ b/LayoutTests/platform/ios/imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView-expected.txt >@@ -0,0 +1,4 @@ >+ >+FAIL Tests scrollend event for scrollIntoView. assert_equals: HTML.scrollIntoView scrollLeft expected 10000 but got 2200 >+PASS Tests scrollend event for nested scrollIntoView. >+ >diff --git a/LayoutTests/platform/mac-wk1/TestExpectations b/LayoutTests/platform/mac-wk1/TestExpectations >index a0ba1ec191..660ebe4c88 100644 >--- a/LayoutTests/platform/mac-wk1/TestExpectations >+++ b/LayoutTests/platform/mac-wk1/TestExpectations >@@ -753,6 +753,8 @@ webkit.org/b/191357 imported/w3c/web-platform-tests/css/cssom-view/scroll-behavi > webkit.org/b/191357 imported/w3c/web-platform-tests/css/cssom-view/scroll-behavior-main-frame-window.html [ Skip ] > webkit.org/b/191357 imported/w3c/web-platform-tests/css/cssom-view/scroll-behavior-subframe-root.html [ Skip ] > webkit.org/b/191357 imported/w3c/web-platform-tests/css/cssom-view/scroll-behavior-subframe-window.html [ Skip ] >+webkit.org/b/191357 imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-programmatic-scroll.html [ Skip ] >+webkit.org/b/191357 imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView.html [ Skip ] > > webkit.org/b/189908 imported/w3c/web-platform-tests/resource-timing/resource_timing.worker.html [ Failure ] > >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 5f2c278803..22d915e006 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,92 @@ >+2020-04-08 Cathie Chen <cathiechen@igalia.com> >+ >+ Add support for scrollend event of programmatic scrolling >+ https://bugs.webkit.org/show_bug.cgi?id=209134 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add support for scrollend event of programmatic scrolling. m_pendingScrollendEventTargetList in Document stores >+ the scrolled targets which need to dispatch scrollend events. FrameView and RenderLayer provide three ways to >+ add elements to the scrollend list. >+ 1. Instant scrolling including the scrolling position not changed animated scroll, the element would add to the list >+ after scroll position changed. >+ 2. Animated scrolling, the element would be set as the m_scrollEndEventTarget of ScrollAnimation and add to the list >+ when animation stops. For scrollRectIntoView, in order to make sure scrollend event fired after all ancestor layers >+ finishing scrolling, add m_duringScrollRectIntoView to indicate if scrollRectIntoView finished. When ScrollAnimation >+ stops, it will try to find a duringScrollRectIntoView ancestor layer and pass the m_scrollEndEventTarget to its >+ ScrollAnimation. If no such layer found, add m_scrollEndEventTarget to the scrollend list. >+ 3. User asynchronous scrolling, the scroll position wouldn't change immediately. So saveScrollEndEventPosition >+ then checkScrollEndEventPosition if scroll position changed. The programmatic scrolling wouldn't trigger this way. >+ >+ Test: imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-programmatic-scroll.html >+ imported/w3c/web-platform-tests/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView.html >+ >+ * dom/Document.cpp: >+ (WebCore::Document::commonTeardown): >+ (WebCore::Document::addPendingScrollEndEventTarget): Append the targets to m_pendingScrollEndEventTargetList. >+ (WebCore::Document::runScrollSteps): Dispatch scrollend event for each target. >+ * dom/Document.h: Add m_pendingScrollEndEventTargetList which keeps the scrollend event targets. >+ * dom/Element.cpp: >+ (WebCore::Element::setScrollLeft): Set RequestScrollEndEvent::Yes for FrameView element. >+ (WebCore::Element::setScrollTop): Ditto. >+ * page/DOMWindow.cpp: >+ (WebCore::DOMWindow::scrollTo const): Add document to the scrollend list for scrolling to (0, 0) optimization. >+ * page/FrameView.cpp: >+ (WebCore::FrameView::setScrollPosition): Add document to the scrollend list if it scrolled(instant scroll or smooth scroll >+ without changing the position, otherwise set document as the scrollEndTarget of scrollAniamtion.). >+ (WebCore::FrameView::saveScrollEndEventPosition): Save the scroll position that requires to fire an scrollend event. This is for asynchronous scroll. >+ (WebCore::FrameView::checkScrollEndEventPosition): Add document to the list if the scroll position equal to the preserved one. >+ (WebCore::FrameView::addPendingScrollEndEventTarget): Add Document to m_pendingScrollendEventTargetList in Document. >+ * page/FrameView.h: Add m_duringScrollRectIntoView to indicate FrameView is performing a animated scrollRectIntoView scroll. >+ * platform/ScrollAnimation.h: >+ (WebCore::ScrollAnimation::setScrollEndTarget): >+ * platform/ScrollAnimationSmooth.cpp: >+ (WebCore::ScrollAnimationSmooth::stop): Call notifyScrollEnd. >+ (WebCore::ScrollAnimationSmooth::notifyScrollEnd): setDuringScrollRectIntoView false. If there's m_scrollEndEventTarget, try to find an ancestor layer >+ duringScrollRectIntoView and set the target to it. Otherwise, add the target to the scrollend list. >+ (WebCore::ScrollAnimationSmooth::setScrollEndTarget): >+ (WebCore::ScrollAnimationSmooth::animationTimerFired): Call notifyScrollEnd if not continueAnimation. >+ * platform/ScrollAnimationSmooth.h: Add m_scrollEndEventTarget. >+ * platform/ScrollAnimator.cpp: >+ (WebCore::ScrollAnimator::setScrollEndTarget): Set m_scrollEndEventTarget to ScrollAnimationSmooth. >+ * platform/ScrollAnimator.h: >+ * platform/ScrollTypes.h: Add RequestScrollEndEvent. >+ * platform/ScrollView.cpp: >+ (WebCore::ScrollView::setContentsScrollPosition): Add RequestScrollEndEvent::Yes. >+ (WebCore::ScrollView::scrollTo): Call checkScrollEndEventPosition if the scroll position changed. >+ (WebCore::ScrollView::setScrollPosition): >+ * platform/ScrollView.h: >+ * platform/ScrollableArea.cpp: >+ (WebCore::ScrollableArea::setScrollEndTarget): >+ * platform/ScrollableArea.h: >+ (WebCore::ScrollableArea::saveScrollEndEventPosition): >+ (WebCore::ScrollableArea::checkScrollEndEventPosition): >+ (WebCore::ScrollableArea::addPendingScrollEndEventTarget): >+ (WebCore::ScrollableArea::duringScrollRectIntoView): >+ (WebCore::ScrollableArea::setDuringScrollRectIntoView): >+ (WebCore::ScrollableArea::findAncestorDuringScrollRectIntoView): >+ * rendering/RenderBox.cpp: >+ (WebCore::RenderBox::setScrollPosition): >+ * rendering/RenderLayer.cpp: >+ (WebCore::RenderLayer::scrollToXPosition): >+ (WebCore::RenderLayer::scrollToYPosition): >+ (WebCore::RenderLayer::setScrollPosition): Add element to the scrollend list if it scrolled(instant scroll or smooth scroll >+ without changing the position, otherwise set element as the scrollEndTarget of scrollAniamtion.). >+ >+ (WebCore::RenderLayer::scrollToOffset): Call checkScrollEndEventPosition, if there is no scrolling. >+ (WebCore::RenderLayer::scrollTo): checkScrollEndEventPosition if scroll position changed. >+ (WebCore::RenderLayer::scrollRectToVisible): setDuringScrollRectIntoView(true) if it's performing a animated scrolling. >+ After parentLayer->scrollRectToVisible(), check if current layer is duringScrollRectIntoView, set the element as animation's scrollend Target. >+ Otherwise, find the duringScrollRectIntoView ancestor layer and pass the element to ancestor layer's scrollAnimation. >+ If no duringScrollRectIntoView layer found, add the element to the scrollend list. >+ (WebCore::RenderLayer::saveScrollEndEventPosition): >+ (WebCore::RenderLayer::checkScrollEndEventPosition): >+ (WebCore::RenderLayer::addPendingScrollEndEventTarget): >+ (WebCore::RenderLayer::findAncestorDuringScrollRectIntoView): Find the duringScrollRectIntoView ancestor layer, and check FrameView at the end. >+ * rendering/RenderLayer.h: >+ * rendering/RenderObject.cpp: >+ (WebCore::RenderObject::scrollRectToVisible): Add RequestScrollEndEvent::Yes. >+ > 2020-03-09 Cathie Chen <cathiechen@igalia.com> > > Add support for overscroll events parsing >diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp >index 3ef945900b..e5c4787ed2 100644 >--- a/Source/WebCore/dom/Document.cpp >+++ b/Source/WebCore/dom/Document.cpp >@@ -799,6 +799,10 @@ void Document::commonTeardown() > > m_pendingScrollEventTargetList = nullptr; > >+#if ENABLE(OVERSCROLL_EVENTS) >+ m_pendingScrollEndEventTargetList = nullptr; >+#endif >+ > while (!m_timelines.computesEmpty()) > m_timelines.begin()->detachFromDocument(); > m_timeline = nullptr; >@@ -4072,6 +4076,26 @@ void Document::addPendingScrollEventTarget(ContainerNode& target) > targets.append(target); > } > >+#if ENABLE(OVERSCROLL_EVENTS) >+void Document::addPendingScrollEndEventTarget(ContainerNode& target) >+{ >+ if (!settings().overscrollEventsEnabled()) >+ return; >+ >+ if (!m_pendingScrollEndEventTargetList) >+ m_pendingScrollEndEventTargetList = makeUnique<PendingScrollEventTargetList>(); >+ >+ auto& targets = m_pendingScrollEndEventTargetList->targets; >+ if (targets.findMatching([&] (auto& entry) { return entry.ptr() == ⌖ }) != notFound) >+ return; >+ >+ if (targets.isEmpty()) >+ scheduleTimedRenderingUpdate(); >+ >+ targets.append(target); >+} >+#endif >+ > void Document::setNeedsVisualViewportScrollEvent() > { > if (!m_needsVisualViewportScrollEvent) >@@ -4097,6 +4121,18 @@ void Document::runScrollSteps() > if (auto* window = domWindow()) > window->visualViewport().dispatchEvent(Event::create(eventNames().scrollEvent, Event::CanBubble::No, Event::IsCancelable::No)); > } >+ >+#if ENABLE(OVERSCROLL_EVENTS) >+ // Deal with scrollend event. >+ if (m_pendingScrollEndEventTargetList && !m_pendingScrollEndEventTargetList->targets.isEmpty()) { >+ LOG_WITH_STREAM(Events, stream << "Document" << this << "sending scrollend events to pending scroll event targets"); >+ auto currentScrollEndTargets = WTFMove(m_pendingScrollEndEventTargetList->targets); >+ for (auto& target : currentScrollEndTargets) { >+ auto bubbles = target->isDocumentNode() ? Event::CanBubble::Yes : Event::CanBubble::No; >+ target->dispatchEvent(Event::create(eventNames().scrollendEvent, bubbles, Event::IsCancelable::No)); >+ } >+ } >+#endif > } > > void Document::addAudioProducer(MediaProducer& audioProducer) >diff --git a/Source/WebCore/dom/Document.h b/Source/WebCore/dom/Document.h >index 839afcd58b..745da2df28 100644 >--- a/Source/WebCore/dom/Document.h >+++ b/Source/WebCore/dom/Document.h >@@ -1360,6 +1360,9 @@ public: > void runResizeSteps(); > > void addPendingScrollEventTarget(ContainerNode&); >+#if ENABLE(OVERSCROLL_EVENTS) >+ void addPendingScrollEndEventTarget(ContainerNode&); >+#endif > void setNeedsVisualViewportScrollEvent(); > void runScrollSteps(); > >@@ -2042,6 +2045,9 @@ private: > > struct PendingScrollEventTargetList; > std::unique_ptr<PendingScrollEventTargetList> m_pendingScrollEventTargetList; >+#if ENABLE(OVERSCROLL_EVENTS) >+ std::unique_ptr<PendingScrollEventTargetList> m_pendingScrollEndEventTargetList; >+#endif > > #if ENABLE(MEDIA_STREAM) > HashSet<HTMLMediaElement*> m_mediaStreamStateChangeElements; >diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp >index d90213466c..74c4465167 100644 >--- a/Source/WebCore/dom/Element.cpp >+++ b/Source/WebCore/dom/Element.cpp >@@ -1295,7 +1295,11 @@ void Element::setScrollLeft(int newLeft) > // See https://bugs.webkit.org/show_bug.cgi?id=205059 > AnimatedScroll animated = useSmoothScrolling(ScrollBehavior::Auto, document().documentElement()) ? AnimatedScroll::Yes : AnimatedScroll::No; > IntPoint position(static_cast<int>(newLeft * frame->pageZoomFactor() * frame->frameScaleFactor()), frame->view()->scrollY()); >+#if ENABLE(OVERSCROLL_EVENTS) >+ frame->view()->setScrollPosition(position, ScrollClamping::Clamped, animated, RequestScrollEndEvent::Yes); >+#else > frame->view()->setScrollPosition(position, ScrollClamping::Clamped, animated); >+#endif > } > return; > } >@@ -1319,7 +1323,11 @@ void Element::setScrollTop(int newTop) > // See https://bugs.webkit.org/show_bug.cgi?id=205059 > AnimatedScroll animated = useSmoothScrolling(ScrollBehavior::Auto, document().documentElement()) ? AnimatedScroll::Yes : AnimatedScroll::No; > IntPoint position(frame->view()->scrollX(), static_cast<int>(newTop * frame->pageZoomFactor() * frame->frameScaleFactor())); >+#if ENABLE(OVERSCROLL_EVENTS) >+ frame->view()->setScrollPosition(position, ScrollClamping::Clamped, animated, RequestScrollEndEvent::Yes); >+#else > frame->view()->setScrollPosition(position, ScrollClamping::Clamped, animated); >+#endif > } > return; > } >diff --git a/Source/WebCore/page/DOMWindow.cpp b/Source/WebCore/page/DOMWindow.cpp >index 09aab6b23d..62fde6e7c3 100644 >--- a/Source/WebCore/page/DOMWindow.cpp >+++ b/Source/WebCore/page/DOMWindow.cpp >@@ -1679,8 +1679,15 @@ void DOMWindow::scrollTo(const ScrollToOptions& options, ScrollClamping clamping > > // This is an optimization for the common case of scrolling to (0, 0) when the scroller is already at the origin. > // If an animated scroll is in progress, this optimization is skipped to ensure that the animated scroll is really stopped. >- if (view->currentScrollBehaviorStatus() == ScrollBehaviorStatus::NotInAnimation && !scrollToOptions.left.value() && !scrollToOptions.top.value() && view->contentsScrollPosition() == IntPoint(0, 0)) >+ if (view->currentScrollBehaviorStatus() == ScrollBehaviorStatus::NotInAnimation >+ && !scrollToOptions.left.value() >+ && !scrollToOptions.top.value() >+ && view->contentsScrollPosition() == IntPoint(0, 0)) { >+#if ENABLE(OVERSCROLL_EVENTS) >+ view->addPendingScrollEndEventTarget(); >+#endif > return; >+ } > > document()->updateLayoutIgnorePendingStylesheets(); > >diff --git a/Source/WebCore/page/FrameView.cpp b/Source/WebCore/page/FrameView.cpp >index 7441d0c402..fdf8343d8c 100644 >--- a/Source/WebCore/page/FrameView.cpp >+++ b/Source/WebCore/page/FrameView.cpp >@@ -2275,7 +2275,7 @@ void FrameView::scrollElementToRect(const Element& element, const IntRect& rect) > setScrollPosition(IntPoint(bounds.x() - centeringOffsetX - rect.x(), bounds.y() - centeringOffsetY - rect.y())); > } > >-void FrameView::setScrollPosition(const ScrollPosition& scrollPosition, ScrollClamping clamping, AnimatedScroll animated) >+void FrameView::setScrollPosition(const ScrollPosition& scrollPosition, ScrollClamping clamping, AnimatedScroll animated, RequestScrollEndEvent requestScrollEndEvent) > { > LOG_WITH_STREAM(Scrolling, stream << "FrameView::setScrollPosition " << scrollPosition << " , clearing anchor"); > >@@ -2288,10 +2288,21 @@ void FrameView::setScrollPosition(const ScrollPosition& scrollPosition, ScrollCl > Page* page = frame().page(); > if (page && page->isMonitoringWheelEvents()) > scrollAnimator().setWheelEventTestMonitor(page->wheelEventTestMonitor()); >+ >+ ScrollPosition newScrollPosition = (!delegatesScrolling() && clamping == ScrollClamping::Clamped) ? adjustScrollPositionWithinRange(scrollPosition) : scrollPosition; > if (animated == AnimatedScroll::Yes) >- scrollToOffsetWithAnimation(scrollOffsetFromPosition(scrollPosition), currentScrollType(), clamping); >+ scrollToOffsetWithAnimation(scrollOffsetFromPosition(newScrollPosition), currentScrollType(), clamping); > else >- ScrollView::setScrollPosition(scrollPosition, clamping); >+ ScrollView::setScrollPosition(newScrollPosition, clamping, animated, requestScrollEndEvent); >+ >+#if ENABLE(OVERSCROLL_EVENTS) >+ if (frame().settings().overscrollEventsEnabled() && requestScrollEndEvent == RequestScrollEndEvent::Yes) { >+ if (newScrollPosition == ScrollView::scrollPosition()) >+ addPendingScrollEndEventTarget(); >+ else if (animated == AnimatedScroll::Yes) >+ setScrollEndTarget(makeWeakPtr(frame().document())); >+ } >+#endif > > setCurrentScrollType(oldScrollType); > } >@@ -3728,6 +3739,32 @@ void FrameView::scrollToOffsetWithAnimation(const ScrollOffset& offset, ScrollTy > setCurrentScrollType(previousScrollType); > } > >+#if ENABLE(OVERSCROLL_EVENTS) >+void FrameView::saveScrollEndEventPosition(const ScrollPosition& position, ScrollClamping clamping) >+{ >+ if (!frame().settings().overscrollEventsEnabled()) >+ return; >+ >+ ScrollPosition clampedScrollPosition = clamping == ScrollClamping::Clamped ? adjustScrollPositionWithinRange(position) : position; >+ m_fireScrollEndEventAtPosition = clampedScrollPosition; >+} >+ >+void FrameView::checkScrollEndEventPosition(const ScrollPosition& position) >+{ >+ if (!m_fireScrollEndEventAtPosition || m_fireScrollEndEventAtPosition.value() != position) >+ return; >+ >+ addPendingScrollEndEventTarget(); >+ m_fireScrollEndEventAtPosition = WTF::nullopt; >+} >+ >+void FrameView::addPendingScrollEndEventTarget() >+{ >+ if (Document* document = frame().document()) >+ document->addPendingScrollEndEventTarget(*document); >+} >+#endif >+ > float FrameView::adjustScrollStepForFixedContent(float step, ScrollbarOrientation orientation, ScrollGranularity granularity) > { > if (granularity != ScrollByPage || orientation == HorizontalScrollbar) >diff --git a/Source/WebCore/page/FrameView.h b/Source/WebCore/page/FrameView.h >index 04ae59a610..47365c445b 100644 >--- a/Source/WebCore/page/FrameView.h >+++ b/Source/WebCore/page/FrameView.h >@@ -229,7 +229,7 @@ public: > #if USE(COORDINATED_GRAPHICS) > WEBCORE_EXPORT void setFixedVisibleContentRect(const IntRect&) final; > #endif >- WEBCORE_EXPORT void setScrollPosition(const ScrollPosition&, ScrollClamping = ScrollClamping::Clamped, AnimatedScroll = AnimatedScroll::No) final; >+ WEBCORE_EXPORT void setScrollPosition(const ScrollPosition&, ScrollClamping = ScrollClamping::Clamped, AnimatedScroll = AnimatedScroll::No, RequestScrollEndEvent = RequestScrollEndEvent::No) final; > void restoreScrollbar(); > void scheduleScrollToFocusedElement(SelectionRevealMode); > void scrollToFocusedElementImmediatelyIfNeeded(); >@@ -664,6 +664,14 @@ public: > > bool inUpdateEmbeddedObjects() const { return m_inUpdateEmbeddedObjects; } > >+#if ENABLE(OVERSCROLL_EVENTS) >+ void saveScrollEndEventPosition(const ScrollPosition&, ScrollClamping = ScrollClamping::Clamped) final; >+ void checkScrollEndEventPosition(const ScrollPosition&) final; >+ void addPendingScrollEndEventTarget() final; >+ bool duringScrollRectIntoView() final { return m_duringScrollRectIntoView; } >+ void setDuringScrollRectIntoView(bool scrolling) final { m_duringScrollRectIntoView = scrolling; } >+#endif >+ > protected: > bool scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) final; > void scrollContentsSlowPath(const IntRect& updateRect) final; >@@ -904,6 +912,10 @@ private: > > Pagination m_pagination; > >+#if ENABLE(OVERSCROLL_EVENTS) >+ Optional<ScrollPosition> m_fireScrollEndEventAtPosition; >+#endif >+ > enum class ViewportRendererType : uint8_t { None, Document, Body }; > ViewportRendererType m_viewportRendererType { ViewportRendererType::None }; > ScrollPinningBehavior m_scrollPinningBehavior { DoNotPin }; >@@ -949,6 +961,10 @@ private: > // True if autosize has been run since m_shouldAutoSize was set. > bool m_didRunAutosize { false }; > bool m_inUpdateEmbeddedObjects { false }; >+ >+#if ENABLE(OVERSCROLL_EVENTS) >+ bool m_duringScrollRectIntoView { false }; >+#endif > }; > > inline void FrameView::incrementVisuallyNonEmptyPixelCount(const IntSize& size) >diff --git a/Source/WebCore/platform/ScrollAnimation.h b/Source/WebCore/platform/ScrollAnimation.h >index 523a77f335..db53f1df7e 100644 >--- a/Source/WebCore/platform/ScrollAnimation.h >+++ b/Source/WebCore/platform/ScrollAnimation.h >@@ -26,6 +26,7 @@ > #ifndef ScrollAnimation_h > #define ScrollAnimation_h > >+#include "ContainerNode.h" > #include "ScrollTypes.h" > > namespace WebCore { >@@ -44,6 +45,9 @@ public: > virtual void updateVisibleLengths() { }; > virtual void setCurrentPosition(const FloatPoint&) { }; > virtual void serviceAnimation() { }; >+#if ENABLE(OVERSCROLL_EVENTS) >+ virtual void setScrollEndTarget(WeakPtr<ContainerNode>) { }; >+#endif > > protected: > ScrollAnimation(ScrollableArea& scrollableArea) >diff --git a/Source/WebCore/platform/ScrollAnimationSmooth.cpp b/Source/WebCore/platform/ScrollAnimationSmooth.cpp >index 2ca578a30f..44e88ab044 100644 >--- a/Source/WebCore/platform/ScrollAnimationSmooth.cpp >+++ b/Source/WebCore/platform/ScrollAnimationSmooth.cpp >@@ -28,7 +28,10 @@ > #include "config.h" > #include "ScrollAnimationSmooth.h" > >+#include "ContainerNode.h" >+#include "Document.h" > #include "FloatPoint.h" >+#include "RenderLayer.h" > #include "ScrollableArea.h" > > namespace WebCore { >@@ -80,10 +83,39 @@ void ScrollAnimationSmooth::scroll(const FloatPoint& position) > > void ScrollAnimationSmooth::stop() > { >+#if ENABLE(OVERSCROLL_EVENTS) >+ if (m_animationTimer.isActive()) >+ notifyScrollEnd(); >+#endif >+ > m_animationTimer.stop(); > m_scrollableArea.setScrollBehaviorStatus(ScrollBehaviorStatus::NotInAnimation); > } > >+#if ENABLE(OVERSCROLL_EVENTS) >+void ScrollAnimationSmooth::notifyScrollEnd() >+{ >+ if (!m_scrollEndEventTarget) { >+ m_scrollableArea.setDuringScrollRectIntoView(false); >+ return; >+ } >+ >+ auto* duringScrollingParentLayer = m_scrollableArea.duringScrollRectIntoView() ? m_scrollableArea.findAncestorDuringScrollRectIntoView() : nullptr; >+ if (duringScrollingParentLayer) >+ duringScrollingParentLayer->setScrollEndTarget(m_scrollEndEventTarget); >+ else >+ m_scrollEndEventTarget->document().addPendingScrollEndEventTarget(*m_scrollEndEventTarget); >+ >+ m_scrollableArea.setDuringScrollRectIntoView(false); >+ m_scrollEndEventTarget = nullptr; >+} >+ >+void ScrollAnimationSmooth::setScrollEndTarget(WeakPtr<ContainerNode> target) >+{ >+ m_scrollEndEventTarget = target; >+} >+#endif >+ > void ScrollAnimationSmooth::updateVisibleLengths() > { > m_horizontalData.visibleLength = m_scrollableArea.visibleWidth(); >@@ -410,8 +442,12 @@ void ScrollAnimationSmooth::animationTimerFired() > > if (continueAnimation) > startNextTimer(std::max(minimumTimerInterval, deltaToNextFrame)); >- else >+ else { > m_scrollableArea.setScrollBehaviorStatus(ScrollBehaviorStatus::NotInAnimation); >+#if ENABLE(OVERSCROLL_EVENTS) >+ notifyScrollEnd(); >+#endif >+ } > > m_notifyPositionChangedFunction(FloatPoint(m_horizontalData.currentPosition, m_verticalData.currentPosition)); > } >diff --git a/Source/WebCore/platform/ScrollAnimationSmooth.h b/Source/WebCore/platform/ScrollAnimationSmooth.h >index 8173fa7e20..184cfad1d1 100644 >--- a/Source/WebCore/platform/ScrollAnimationSmooth.h >+++ b/Source/WebCore/platform/ScrollAnimationSmooth.h >@@ -31,6 +31,7 @@ > > namespace WebCore { > >+class ContainerNode; > class FloatPoint; > class ScrollableArea; > enum class ScrollClamping : uint8_t; >@@ -48,10 +49,16 @@ public: > Bounce > }; > >+#if ENABLE(OVERSCROLL_EVENTS) >+ void setScrollEndTarget(WeakPtr<ContainerNode>) override; >+#endif > private: > bool scroll(ScrollbarOrientation, ScrollGranularity, float step, float multiplier) override; > void scroll(const FloatPoint&) override; > void stop() override; >+#if ENABLE(OVERSCROLL_EVENTS) >+ void notifyScrollEnd(); >+#endif > void updateVisibleLengths() override; > void setCurrentPosition(const FloatPoint&) override; > >@@ -104,6 +111,9 @@ private: > > MonotonicTime m_startTime; > Timer m_animationTimer; >+#if ENABLE(OVERSCROLL_EVENTS) >+ WeakPtr<ContainerNode> m_scrollEndEventTarget; >+#endif > }; > > } // namespace WebCore >diff --git a/Source/WebCore/platform/ScrollAnimator.cpp b/Source/WebCore/platform/ScrollAnimator.cpp >index 0fc729e912..d893008aa0 100644 >--- a/Source/WebCore/platform/ScrollAnimator.cpp >+++ b/Source/WebCore/platform/ScrollAnimator.cpp >@@ -287,5 +287,10 @@ void ScrollAnimator::didAddHorizontalScrollbar(Scrollbar*) > { > m_animationProgrammaticScroll->updateVisibleLengths(); > } >- >+#if ENABLE(OVERSCROLL_EVENTS) >+void ScrollAnimator::setScrollEndTarget(WeakPtr<ContainerNode> target) >+{ >+ m_animationProgrammaticScroll->setScrollEndTarget(target); >+} >+#endif > } // namespace WebCore >diff --git a/Source/WebCore/platform/ScrollAnimator.h b/Source/WebCore/platform/ScrollAnimator.h >index 9b7c09b690..eede798db5 100644 >--- a/Source/WebCore/platform/ScrollAnimator.h >+++ b/Source/WebCore/platform/ScrollAnimator.h >@@ -37,6 +37,7 @@ > #include "WheelEventTestMonitor.h" > #include <wtf/FastMalloc.h> > #include <wtf/Forward.h> >+#include <wtf/WeakPtr.h> > > #if ENABLE(RUBBER_BANDING) || ENABLE(CSS_SCROLL_SNAP) > #include "ScrollController.h" >@@ -44,6 +45,7 @@ > > namespace WebCore { > >+class ContainerNode; > class FloatPoint; > class PlatformTouchEvent; > class ScrollAnimation; >@@ -143,6 +145,10 @@ public: > FloatSize viewportSize() const override; > #endif > >+#if ENABLE(OVERSCROLL_EVENTS) >+ void setScrollEndTarget(WeakPtr<ContainerNode>); >+#endif >+ > protected: > virtual void notifyPositionChanged(const FloatSize& delta); > void updateActiveScrollSnapIndexForOffset(); >diff --git a/Source/WebCore/platform/ScrollTypes.h b/Source/WebCore/platform/ScrollTypes.h >index b975fb0430..562734547b 100644 >--- a/Source/WebCore/platform/ScrollTypes.h >+++ b/Source/WebCore/platform/ScrollTypes.h >@@ -65,6 +65,11 @@ enum class AnimatedScroll : uint8_t { > Yes > }; > >+enum class RequestScrollEndEvent : uint8_t { >+ No, >+ Yes >+}; >+ > inline ScrollDirection logicalToPhysical(ScrollLogicalDirection direction, bool isVertical, bool isFlipped) > { > switch (direction) { >diff --git a/Source/WebCore/platform/ScrollView.cpp b/Source/WebCore/platform/ScrollView.cpp >index d81d2444c0..6bf64aad54 100644 >--- a/Source/WebCore/platform/ScrollView.cpp >+++ b/Source/WebCore/platform/ScrollView.cpp >@@ -212,7 +212,11 @@ void ScrollView::setContentsScrollPosition(const IntPoint& position, ScrollClamp > if (platformWidget()) > setActualScrollPosition(position); > #endif >+#if ENABLE(OVERSCROLL_EVENTS) >+ setScrollPosition(position, clamping, animated, RequestScrollEndEvent::Yes); >+#else > setScrollPosition(position, clamping, animated); >+#endif > } > > FloatRect ScrollView::exposedContentRect() const >@@ -486,6 +490,10 @@ void ScrollView::scrollTo(const ScrollPosition& newPosition) > > m_scrollPosition = newPosition; > >+#if ENABLE(OVERSCROLL_EVENTS) >+ checkScrollEndEventPosition(m_scrollPosition); >+#endif >+ > if (scrollbarsSuppressed()) > return; > >@@ -513,7 +521,7 @@ void ScrollView::completeUpdatesAfterScrollTo(const IntSize& scrollDelta) > updateCompositingLayersAfterScrolling(); > } > >-void ScrollView::setScrollPosition(const ScrollPosition& scrollPosition, ScrollClamping clamping, AnimatedScroll/* animated*/) >+void ScrollView::setScrollPosition(const ScrollPosition& scrollPosition, ScrollClamping clamping, AnimatedScroll /* animated*/, RequestScrollEndEvent) > { > LOG_WITH_STREAM(Scrolling, stream << "ScrollView::setScrollPosition " << scrollPosition); > >diff --git a/Source/WebCore/platform/ScrollView.h b/Source/WebCore/platform/ScrollView.h >index 060424c921..77a77561aa 100644 >--- a/Source/WebCore/platform/ScrollView.h >+++ b/Source/WebCore/platform/ScrollView.h >@@ -261,7 +261,7 @@ public: > ScrollPosition cachedScrollPosition() const { return m_cachedScrollPosition; } > > // Functions for scrolling the view. >- virtual void setScrollPosition(const ScrollPosition&, ScrollClamping = ScrollClamping::Clamped, AnimatedScroll = AnimatedScroll::No); >+ virtual void setScrollPosition(const ScrollPosition&, ScrollClamping = ScrollClamping::Clamped, AnimatedScroll = AnimatedScroll::No, RequestScrollEndEvent = RequestScrollEndEvent::No); > > void scrollBy(const IntSize& s) { return setScrollPosition(scrollPosition() + s); } > >diff --git a/Source/WebCore/platform/ScrollableArea.cpp b/Source/WebCore/platform/ScrollableArea.cpp >index 5bda857bb9..20fbe42679 100644 >--- a/Source/WebCore/platform/ScrollableArea.cpp >+++ b/Source/WebCore/platform/ScrollableArea.cpp >@@ -32,6 +32,7 @@ > #include "config.h" > #include "ScrollableArea.h" > >+#include "ContainerNode.h" > #include "FloatPoint.h" > #include "GraphicsContext.h" > #include "GraphicsLayer.h" >@@ -769,4 +770,11 @@ void ScrollableArea::computeScrollbarValueAndOverhang(float currentPosition, flo > } > } > >+#if ENABLE(OVERSCROLL_EVENTS) >+void ScrollableArea::setScrollEndTarget(WeakPtr<ContainerNode> target) >+{ >+ scrollAnimator().setScrollEndTarget(target); >+} >+#endif >+ > } // namespace WebCore >diff --git a/Source/WebCore/platform/ScrollableArea.h b/Source/WebCore/platform/ScrollableArea.h >index d203aebe5c..3331679b79 100644 >--- a/Source/WebCore/platform/ScrollableArea.h >+++ b/Source/WebCore/platform/ScrollableArea.h >@@ -33,6 +33,7 @@ > > namespace WebCore { > >+class ContainerNode; > class FloatPoint; > class GraphicsContext; > class LayoutPoint; >@@ -346,6 +347,16 @@ public: > > virtual bool shouldPlaceBlockDirectionScrollbarOnLeft() const = 0; > >+#if ENABLE(OVERSCROLL_EVENTS) >+ virtual void saveScrollEndEventPosition(const ScrollPosition&, ScrollClamping = ScrollClamping::Clamped) { } >+ virtual void checkScrollEndEventPosition(const ScrollPosition&) { } >+ virtual void addPendingScrollEndEventTarget() { } >+ virtual bool duringScrollRectIntoView() { return false; } >+ virtual void setDuringScrollRectIntoView(bool) { } >+ virtual ScrollableArea* findAncestorDuringScrollRectIntoView() { return nullptr; } >+ void setScrollEndTarget(WeakPtr<ContainerNode>); >+#endif >+ > protected: > WEBCORE_EXPORT ScrollableArea(); > WEBCORE_EXPORT virtual ~ScrollableArea(); >diff --git a/Source/WebCore/rendering/RenderBox.cpp b/Source/WebCore/rendering/RenderBox.cpp >index 8b286425c0..e3ab74bb17 100644 >--- a/Source/WebCore/rendering/RenderBox.cpp >+++ b/Source/WebCore/rendering/RenderBox.cpp >@@ -602,7 +602,11 @@ void RenderBox::setScrollPosition(const ScrollPosition& position, ScrollType scr > if (!hasOverflowClip() || !layer()) > return; > setupWheelEventMonitor(*layer()); >+#if ENABLE(OVERSCROLL_EVENTS) >+ layer()->setScrollPosition(position, scrollType, clamping, animated, RequestScrollEndEvent::Yes); >+#else > layer()->setScrollPosition(position, scrollType, clamping, animated); >+#endif > } > > void RenderBox::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const >diff --git a/Source/WebCore/rendering/RenderLayer.cpp b/Source/WebCore/rendering/RenderLayer.cpp >index be2317a782..d8c0b155e5 100644 >--- a/Source/WebCore/rendering/RenderLayer.cpp >+++ b/Source/WebCore/rendering/RenderLayer.cpp >@@ -2624,21 +2624,44 @@ void RenderLayer::applyPostLayoutScrollPositionIfNeeded() > void RenderLayer::scrollToXPosition(int x, ScrollType scrollType, ScrollClamping clamping, AnimatedScroll animated) > { > ScrollPosition position(x, m_scrollPosition.y()); >+#if ENABLE(OVERSCROLL_EVENTS) >+ setScrollPosition(position, scrollType, clamping, animated, RequestScrollEndEvent::Yes); >+#else > setScrollPosition(position, scrollType, clamping, animated); >+#endif > } > > void RenderLayer::scrollToYPosition(int y, ScrollType scrollType, ScrollClamping clamping, AnimatedScroll animated) > { > ScrollPosition position(m_scrollPosition.x(), y); >+#if ENABLE(OVERSCROLL_EVENTS) >+ setScrollPosition(position, scrollType, clamping, animated, RequestScrollEndEvent::Yes); >+#else > setScrollPosition(position, scrollType, clamping, animated); >+#endif > } > >-void RenderLayer::setScrollPosition(const ScrollPosition& position, ScrollType scrollType, ScrollClamping clamping, AnimatedScroll animated) >+void RenderLayer::setScrollPosition(const ScrollPosition& position, ScrollType scrollType, ScrollClamping clamping, AnimatedScroll animated, RequestScrollEndEvent requestScrollEndEvent) > { >+#if !ENABLE(OVERSCROLL_EVENTS) >+ UNUSED_PARAM(requestScrollEndEvent); >+#endif >+ >+ auto offset = scrollOffsetFromPosition(position); >+ ScrollOffset newScrollOffset = clamping == ScrollClamping::Clamped ? clampScrollOffset(offset) : offset; > if (animated == AnimatedScroll::Yes) >- scrollToOffsetWithAnimation(scrollOffsetFromPosition(position), scrollType, clamping); >+ scrollToOffsetWithAnimation(newScrollOffset, scrollType, clamping); > else >- scrollToOffset(scrollOffsetFromPosition(position), scrollType, clamping); >+ scrollToOffset(newScrollOffset, scrollType, clamping); >+ >+#if ENABLE(OVERSCROLL_EVENTS) >+ if (renderer().settings().overscrollEventsEnabled() && requestScrollEndEvent == RequestScrollEndEvent::Yes) { >+ if (newScrollOffset == scrollOffset()) >+ addPendingScrollEndEventTarget(); >+ else if (animated == AnimatedScroll::Yes) >+ setScrollEndTarget(makeWeakPtr(renderer().element())); >+ } >+#endif > } > > ScrollOffset RenderLayer::clampScrollOffset(const ScrollOffset& scrollOffset) const >@@ -2661,13 +2684,18 @@ void RenderLayer::scrollToOffset(const ScrollOffset& scrollOffset, ScrollType sc > scrollAnimator().cancelAnimations(); > > ScrollOffset clampedScrollOffset = clamping == ScrollClamping::Clamped ? clampScrollOffset(scrollOffset) : scrollOffset; >- if (clampedScrollOffset == this->scrollOffset()) >+ ScrollPosition clampedScrollPosition = scrollPositionFromOffset(clampedScrollOffset); >+ if (clampedScrollOffset == this->scrollOffset()) { >+#if ENABLE(OVERSCROLL_EVENTS) >+ checkScrollEndEventPosition(clampedScrollPosition); >+#endif > return; >+ } > > auto previousScrollType = currentScrollType(); > setCurrentScrollType(scrollType); > >- if (!requestScrollPositionUpdate(scrollPositionFromOffset(clampedScrollOffset), scrollType, clamping)) >+ if (!requestScrollPositionUpdate(clampedScrollPosition, scrollType, clamping)) > scrollToOffsetWithoutAnimation(clampedScrollOffset, clamping); > setScrollBehaviorStatus(ScrollBehaviorStatus::NotInAnimation); > >@@ -2791,6 +2819,10 @@ void RenderLayer::scrollTo(const ScrollPosition& position) > if (Element* element = renderer().element()) > element->document().addPendingScrollEventTarget(*element); > >+#if ENABLE(OVERSCROLL_EVENTS) >+ checkScrollEndEventPosition(m_scrollPosition); >+#endif >+ > if (scrollsOverflow()) > view.frameView().didChangeScrollOffset(); > >@@ -2836,10 +2868,14 @@ bool RenderLayer::allowsCurrentScroll() const > return box->hasHorizontalOverflow() || box->hasVerticalOverflow(); > } > >-void RenderLayer::scrollRectToVisible(const LayoutRect& absoluteRect, bool insideFixed, const ScrollRectToVisibleOptions& options) >+void RenderLayer::scrollRectToVisible(const LayoutRect& absoluteRect, bool insideFixed, const ScrollRectToVisibleOptions& options, RequestScrollEndEvent requestScrollEndEvent) > { > LOG_WITH_STREAM(Scrolling, stream << "Layer " << this << " scrollRectToVisible " << absoluteRect); > >+#if ENABLE(OVERSCROLL_EVENTS) >+ bool overscrollEventsEnabled = renderer().settings().overscrollEventsEnabled(); >+#endif >+ > LayoutRect newRect = absoluteRect; > FrameView& frameView = renderer().view().frameView(); > auto* parentLayer = enclosingContainingBlockLayer(*this, CrossFrameBoundaries::No); >@@ -2866,6 +2902,10 @@ void RenderLayer::scrollRectToVisible(const LayoutRect& absoluteRect, bool insid > if (autoscrollNotInProgress && useSmoothScrolling(options.behavior, box->element())) > animated = AnimatedScroll::Yes; > setScrollPosition(scrollPositionFromOffset(clampedScrollOffset), ScrollType::Programmatic, ScrollClamping::Clamped, animated); >+#if ENABLE(OVERSCROLL_EVENTS) >+ if (overscrollEventsEnabled && animated == AnimatedScroll::Yes && clampedScrollOffset != scrollOffset()) >+ setDuringScrollRectIntoView(true); >+#endif > IntSize scrollOffsetDifference = clampedScrollOffset - oldScrollOffset; > localExposeRect.move(-scrollOffsetDifference); > newRect = LayoutRect(box->localToAbsoluteQuad(FloatQuad(FloatRect(localExposeRect)), UseTransforms).boundingBox()); >@@ -2898,6 +2938,14 @@ void RenderLayer::scrollRectToVisible(const LayoutRect& absoluteRect, bool insid > animated = AnimatedScroll::Yes; > frameView.setScrollPosition(scrollPosition, ScrollClamping::Clamped, animated); > >+#if ENABLE(OVERSCROLL_EVENTS) >+ if (overscrollEventsEnabled && animated == AnimatedScroll::Yes && scrollPosition != frameView.scrollPosition()) { >+ frameView.setDuringScrollRectIntoView(true); >+ if (requestScrollEndEvent == RequestScrollEndEvent::Yes) >+ frameView.setScrollEndTarget(makeWeakPtr(frameView.frame().document())); >+ } >+#endif >+ > if (options.shouldAllowCrossOriginScrolling == ShouldAllowCrossOriginScrolling::Yes || frameView.safeToPropagateScrollToParent()) { > if (auto* enclosingLayer = ownerElement->renderer()->enclosingLayer()) > parentLayer = enclosingLayer->enclosingScrollableLayer(IncludeSelfOrNot::IncludeSelf, CrossFrameBoundaries::No); >@@ -2942,6 +2990,13 @@ void RenderLayer::scrollRectToVisible(const LayoutRect& absoluteRect, bool insid > if (autoscrollNotInProgress && useSmoothScrolling(options.behavior, renderer().document().documentElement())) > animated = AnimatedScroll::Yes; > frameView.setScrollPosition(clampedScrollPosition, ScrollClamping::Clamped, animated); >+#if ENABLE(OVERSCROLL_EVENTS) >+ if (overscrollEventsEnabled && animated == AnimatedScroll::Yes && clampedScrollPosition != frameView.scrollPosition()) { >+ frameView.setDuringScrollRectIntoView(true); >+ if (requestScrollEndEvent == RequestScrollEndEvent::Yes) >+ frameView.setScrollEndTarget(makeWeakPtr(frameView.frame().document())); >+ } >+#endif > } > > // This is the outermost view of a web page, so after scrolling this view we >@@ -2953,8 +3008,26 @@ void RenderLayer::scrollRectToVisible(const LayoutRect& absoluteRect, bool insid > } > } > >- if (parentLayer) >+ if (parentLayer) { > parentLayer->scrollRectToVisible(newRect, insideFixed, options); >+#if ENABLE(OVERSCROLL_EVENTS) >+ if (overscrollEventsEnabled && requestScrollEndEvent == RequestScrollEndEvent::Yes) { >+ ContainerNode* target = renderer().element(); >+ if (renderer().document().scrollingElement() == target) >+ target = &renderer().document(); >+ >+ if (duringScrollRectIntoView()) >+ setScrollEndTarget(makeWeakPtr(target)); >+ else { >+ auto* duringScrollingParentLayer = findAncestorDuringScrollRectIntoView(); >+ if (duringScrollingParentLayer) >+ duringScrollingParentLayer->setScrollEndTarget(makeWeakPtr(target)); >+ else >+ renderer().document().addPendingScrollEndEventTarget(*target); >+ } >+ } >+#endif >+ } > } > > void RenderLayer::updateCompositingLayersAfterScroll() >@@ -7035,6 +7108,49 @@ void RenderLayer::invalidateEventRegion() > #endif > } > >+#if ENABLE(OVERSCROLL_EVENTS) >+void RenderLayer::saveScrollEndEventPosition(const ScrollPosition& position, ScrollClamping clamping) >+{ >+ if (!renderer().settings().overscrollEventsEnabled()) >+ return; >+ >+ auto offset = scrollOffsetFromPosition(position); >+ ScrollOffset clampedScrollOffset = clamping == ScrollClamping::Clamped ? clampScrollOffset(offset) : offset; >+ m_fireScrollEndEventAtPosition = scrollPositionFromOffset(clampedScrollOffset); >+} >+ >+void RenderLayer::checkScrollEndEventPosition(const ScrollPosition& position) >+{ >+ if (!m_fireScrollEndEventAtPosition || m_fireScrollEndEventAtPosition.value() != position) >+ return; >+ >+ addPendingScrollEndEventTarget(); >+ >+ m_fireScrollEndEventAtPosition = WTF::nullopt; >+} >+ >+void RenderLayer::addPendingScrollEndEventTarget() >+{ >+ if (Element* element = renderer().element()) >+ element->document().addPendingScrollEndEventTarget(*element); >+} >+ >+ScrollableArea* RenderLayer::findAncestorDuringScrollRectIntoView() >+{ >+ auto* parentLayer = enclosingContainingBlockLayer(*this, CrossFrameBoundaries::No); >+ while (parentLayer) { >+ if (parentLayer->duringScrollRectIntoView()) >+ return parentLayer; >+ parentLayer = enclosingContainingBlockLayer(*parentLayer, CrossFrameBoundaries::No); >+ } >+ >+ FrameView& frameView = renderer().view().frameView(); >+ if (frameView.duringScrollRectIntoView()) >+ return &frameView; >+ return nullptr; >+} >+#endif >+ > TextStream& operator<<(WTF::TextStream& ts, ClipRectsType clipRectsType) > { > switch (clipRectsType) { >diff --git a/Source/WebCore/rendering/RenderLayer.h b/Source/WebCore/rendering/RenderLayer.h >index d708d26ed1..501cc6b168 100644 >--- a/Source/WebCore/rendering/RenderLayer.h >+++ b/Source/WebCore/rendering/RenderLayer.h >@@ -452,7 +452,7 @@ public: > > void scrollToXPosition(int x, ScrollType, ScrollClamping = ScrollClamping::Clamped, AnimatedScroll = AnimatedScroll::No); > void scrollToYPosition(int y, ScrollType, ScrollClamping = ScrollClamping::Clamped, AnimatedScroll = AnimatedScroll::No); >- void setScrollPosition(const ScrollPosition&, ScrollType, ScrollClamping = ScrollClamping::Clamped, AnimatedScroll = AnimatedScroll::No); >+ void setScrollPosition(const ScrollPosition&, ScrollType, ScrollClamping = ScrollClamping::Clamped, AnimatedScroll = AnimatedScroll::No, RequestScrollEndEvent = RequestScrollEndEvent::No); > > // These are only used by marquee. > void scrollToXOffset(int x) { scrollToOffset(ScrollOffset(x, scrollOffset().y()), ScrollType::Programmatic, ScrollClamping::Unclamped); } >@@ -467,7 +467,7 @@ public: > void availableContentSizeChanged(AvailableSizeChangeReason) final; > > // "absoluteRect" is in scaled document coordinates. >- void scrollRectToVisible(const LayoutRect& absoluteRect, bool insideFixed, const ScrollRectToVisibleOptions&); >+ void scrollRectToVisible(const LayoutRect& absoluteRect, bool insideFixed, const ScrollRectToVisibleOptions&, RequestScrollEndEvent = RequestScrollEndEvent::No); > > bool scrollsOverflow() const; > bool hasScrollableHorizontalOverflow() const; >@@ -925,6 +925,15 @@ public: > > void invalidateEventRegion(); > >+#if ENABLE(OVERSCROLL_EVENTS) >+ void saveScrollEndEventPosition(const ScrollPosition&, ScrollClamping = ScrollClamping::Clamped) final; >+ void checkScrollEndEventPosition(const ScrollPosition&) final; >+ void addPendingScrollEndEventTarget() final; >+ bool duringScrollRectIntoView() final { return m_duringScrollRectIntoView; } >+ void setDuringScrollRectIntoView(bool scrolling) final { m_duringScrollRectIntoView = scrolling; } >+ ScrollableArea* findAncestorDuringScrollRectIntoView() final; >+#endif >+ > private: > > void setNextSibling(RenderLayer* next) { m_next = next; } >@@ -1281,6 +1290,10 @@ private: > bool m_layerListMutationAllowed : 1; > #endif > >+#if ENABLE(OVERSCROLL_EVENTS) >+ bool m_duringScrollRectIntoView : 1; >+#endif >+ > #if ENABLE(CSS_COMPOSITING) > unsigned m_blendMode : 5; // BlendMode > bool m_hasNotIsolatedCompositedBlendingDescendants : 1; >@@ -1356,6 +1369,10 @@ private: > > std::unique_ptr<RenderLayerFilters> m_filters; > std::unique_ptr<RenderLayerBacking> m_backing; >+ >+#if ENABLE(OVERSCROLL_EVENTS) >+ Optional<ScrollPosition> m_fireScrollEndEventAtPosition; >+#endif > > PaintFrequencyTracker m_paintFrequencyTracker; > }; >diff --git a/Source/WebCore/rendering/RenderObject.cpp b/Source/WebCore/rendering/RenderObject.cpp >index d560287526..0a46064432 100644 >--- a/Source/WebCore/rendering/RenderObject.cpp >+++ b/Source/WebCore/rendering/RenderObject.cpp >@@ -427,7 +427,11 @@ bool RenderObject::scrollRectToVisible(const LayoutRect& absoluteRect, bool insi > if (!enclosingLayer) > return false; > >+#if ENABLE(OVERSCROLL_EVENTS) >+ enclosingLayer->scrollRectToVisible(absoluteRect, insideFixed, options, RequestScrollEndEvent::Yes); >+#else > enclosingLayer->scrollRectToVisible(absoluteRect, insideFixed, options); >+#endif > return true; > } >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 209134
:
393744
|
393745
|
393746
|
393747
|
395799
|
395800
|
395801
|
395802
|
395805
|
395806
|
395939
|
395940
|
395943
|
395944
|
396058
|
396059
|
396062
|
399465
|
399466