Diff
Modified: trunk/Source/WebCore/ChangeLog (280491 => 280492)
--- trunk/Source/WebCore/ChangeLog 2021-07-30 20:25:46 UTC (rev 280491)
+++ trunk/Source/WebCore/ChangeLog 2021-07-30 20:26:51 UTC (rev 280492)
@@ -1,3 +1,37 @@
+2021-07-30 Dana Estra <[email protected]>
+
+ Add key-driven smooth scrolling to macOS
+ https://bugs.webkit.org/show_bug.cgi?id=228009
+
+ Reviewed by Tim Horton.
+
+ Cause keyboard scroll event to trigger start of smooth scroll animation, using same physics implementation as iOS smooth scrolling.
+
+ No tests yet.
+
+ * WebCore.xcodeproj/project.pbxproj:
+ * dom/Node.cpp:
+ (WebCore::Node::defaultEventHandler):
+ * page/EventHandler.cpp:
+ (WebCore::EventHandler::defaultKeyboardEventHandler):
+ (WebCore::EventHandler::scrollDistance):
+ (WebCore::EventHandler::stopKeyboardScrolling):
+ (WebCore::EventHandler::startKeyboardScrolling):
+ * platform/KeyboardScrollingAnimator.cpp: Added.
+ * platform/KeyboardScrollingAnimator.h: Added.
+ * platform/ScrollAnimator.cpp:
+ (WebCore::ScrollAnimator::startAnimationCallback):
+ * platform/ScrollAnimator.h:
+ * platform/ScrollController.cpp:
+ (WebCore::ScrollController::animationCallback):
+ (WebCore::ScrollController::startOrStopAnimationCallbacks):
+ (WebCore::ScrollController::beginKeyboardScrolling):
+ (WebCore::ScrollController::stopKeyboardScrolling):
+ (WebCore::ScrollController::setIsAnimatingKeyboardScrolling):
+ (WebCore::ScrollController::updateKeyboardScrollingAnimatingState):
+ * platform/ScrollController.h:
+ (WebCore::ScrollControllerClient::updateKeyboardScrollPosition):
+
2021-07-30 Chris Dumez <[email protected]>
Document's fallback base URL should be deduced from its creator when URL is about:blank
Modified: trunk/Source/WebCore/Sources.txt (280491 => 280492)
--- trunk/Source/WebCore/Sources.txt 2021-07-30 20:25:46 UTC (rev 280491)
+++ trunk/Source/WebCore/Sources.txt 2021-07-30 20:26:51 UTC (rev 280492)
@@ -1745,6 +1745,7 @@
platform/FileMonitor.cpp
platform/FileStream.cpp
platform/FrameRateMonitor.cpp
+platform/KeyboardScrollingAnimator.cpp
platform/LayoutUnit.cpp
platform/LegacySchemeRegistry.cpp
platform/Length.cpp
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (280491 => 280492)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2021-07-30 20:25:46 UTC (rev 280491)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2021-07-30 20:26:51 UTC (rev 280492)
@@ -673,6 +673,7 @@
1F8756B21E22C3350042C40D /* WebSQLiteDatabaseTrackerClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F8756B11E22BEEF0042C40D /* WebSQLiteDatabaseTrackerClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
1FAFBF1915A5FA7400083A20 /* UTIUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FAFBF1615A5FA5200083A20 /* UTIUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; };
1FC40FBA1655CCB90040F29E /* SubimageCacheWithTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FC40FB71655C5910040F29E /* SubimageCacheWithTimer.h */; };
+ 1FD992F826AA24F90088E596 /* KeyboardScrollingAnimator.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FD992F626AA24F80088E596 /* KeyboardScrollingAnimator.h */; settings = {ATTRIBUTES = (Private, ); }; };
20D629271253690B00081543 /* InspectorInstrumentation.h in Headers */ = {isa = PBXBuildFile; fileRef = 20D629251253690B00081543 /* InspectorInstrumentation.h */; };
225A16B50D5C11E900090295 /* WebEventRegion.h in Headers */ = {isa = PBXBuildFile; fileRef = 225A16B30D5C11E900090295 /* WebEventRegion.h */; settings = {ATTRIBUTES = (Private, ); }; };
228C284510D82500009D0D0E /* ScriptWrappable.h in Headers */ = {isa = PBXBuildFile; fileRef = 228C284410D82500009D0D0E /* ScriptWrappable.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -7013,6 +7014,8 @@
1FAFBF1715A5FA5200083A20 /* UTIUtilities.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = UTIUtilities.mm; sourceTree = "<group>"; };
1FC40FB71655C5910040F29E /* SubimageCacheWithTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SubimageCacheWithTimer.h; sourceTree = "<group>"; };
1FC40FB81655C5910040F29E /* SubimageCacheWithTimer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SubimageCacheWithTimer.cpp; sourceTree = "<group>"; };
+ 1FD992F626AA24F80088E596 /* KeyboardScrollingAnimator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeyboardScrollingAnimator.h; sourceTree = "<group>"; };
+ 1FD992F926AA254D0088E596 /* KeyboardScrollingAnimator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KeyboardScrollingAnimator.cpp; sourceTree = "<group>"; };
20D629241253690B00081543 /* InspectorInstrumentation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorInstrumentation.cpp; sourceTree = "<group>"; };
20D629251253690B00081543 /* InspectorInstrumentation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorInstrumentation.h; sourceTree = "<group>"; };
225A16B30D5C11E900090295 /* WebEventRegion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebEventRegion.h; sourceTree = "<group>"; };
@@ -27552,6 +27555,8 @@
4190F3A3249D152800531C57 /* FrameRateMonitor.h */,
A8748BDF12CBF2DC001FBA41 /* HashTools.h */,
BC3BC29B0E91AB0F00835588 /* HostWindow.h */,
+ 1FD992F926AA254D0088E596 /* KeyboardScrollingAnimator.cpp */,
+ 1FD992F626AA24F80088E596 /* KeyboardScrollingAnimator.h */,
1AE00D57182DAC8D00087DD7 /* KeyedCoding.h */,
E15FF7D418C9553800FE4C87 /* KeypressCommand.h */,
A3D42A841F33BA3600A64B62 /* LayoutUnit.cpp */,
@@ -33333,6 +33338,7 @@
83B74EF61F3E0BF200996BC7 /* KeepaliveRequestTracker.h in Headers */,
85031B440A44EFC700F992E0 /* KeyboardEvent.h in Headers */,
1F020AEF26780FCE0000809A /* KeyboardScroll.h in Headers */,
+ 1FD992F826AA24F90088E596 /* KeyboardScrollingAnimator.h in Headers */,
1AE00D59182DAC8D00087DD7 /* KeyedCoding.h in Headers */,
517A63C51B74318F00E7DCDC /* KeyedDecoderCF.h in Headers */,
517A63C61B74319200E7DCDC /* KeyedEncoderCF.h in Headers */,
Modified: trunk/Source/WebCore/dom/Node.cpp (280491 => 280492)
--- trunk/Source/WebCore/dom/Node.cpp 2021-07-30 20:25:46 UTC (rev 280491)
+++ trunk/Source/WebCore/dom/Node.cpp 2021-07-30 20:26:51 UTC (rev 280492)
@@ -2432,7 +2432,7 @@
if (event.target() != this)
return;
const AtomString& eventType = event.type();
- if (eventType == eventNames().keydownEvent || eventType == eventNames().keypressEvent) {
+ if (eventType == eventNames().keydownEvent || eventType == eventNames().keypressEvent || eventType == eventNames().keyupEvent) {
if (is<KeyboardEvent>(event)) {
if (Frame* frame = document().frame())
frame->eventHandler().defaultKeyboardEventHandler(downcast<KeyboardEvent>(event));
Modified: trunk/Source/WebCore/page/EventHandler.cpp (280491 => 280492)
--- trunk/Source/WebCore/page/EventHandler.cpp 2021-07-30 20:25:46 UTC (rev 280491)
+++ trunk/Source/WebCore/page/EventHandler.cpp 2021-07-30 20:26:51 UTC (rev 280492)
@@ -68,7 +68,7 @@
#include "ImageOverlayController.h"
#include "InspectorInstrumentation.h"
#include "KeyboardEvent.h"
-#include "KeyboardScroll.h"
+#include "KeyboardScrollingAnimator.h"
#include "Logging.h"
#include "MouseEvent.h"
#include "MouseEventWithHitTestResults.h"
@@ -3803,6 +3803,12 @@
if (event.charCode() == ' ')
defaultSpaceEventHandler(event);
}
+ if (event.type() == eventNames().keyupEvent) {
+ m_frame.editor().handleKeyboardEvent(event);
+ if (event.defaultHandled())
+ return;
+ stopKeyboardScrolling();
+ }
}
#if ENABLE(DRAG_SUPPORT)
@@ -4197,7 +4203,7 @@
if (!view)
return;
- bool defaultHandled = m_frame.settings().eventHandlerDrivenSmoothKeyboardScrollingEnabled() ? handleKeyboardScrolling(event) : view->logicalScroll(direction, ScrollByPage);
+ bool defaultHandled = m_frame.settings().eventHandlerDrivenSmoothKeyboardScrollingEnabled() ? startKeyboardScrolling(event) : view->logicalScroll(direction, ScrollByPage);
if (defaultHandled)
event.setDefaultHandled();
}
@@ -4237,7 +4243,7 @@
return m_frame.view()->verticalScrollbar();
return m_frame.view()->horizontalScrollbar();
}();
-
+
switch (granularity) {
case ScrollGranularity::ScrollByLine:
return scrollbar->lineStep();
@@ -4252,62 +4258,28 @@
RELEASE_ASSERT_NOT_REACHED();
}
-bool EventHandler::handleKeyboardScrolling(KeyboardEvent& event)
+void EventHandler::stopKeyboardScrolling()
{
Ref protectedFrame = m_frame;
- // FIXME (bug 227459): This logic does not account for writing-mode.
+ FrameView* view = m_frame.view();
- enum class Key : uint8_t { LeftArrow, RightArrow, UpArrow, DownArrow, Space };
+ KeyboardScrollingAnimator* animator = view->scrollAnimator().keyboardScrollingAnimator();
- Key key;
- if (event.keyIdentifier() == "Left")
- key = Key::LeftArrow;
- else if (event.keyIdentifier() == "Right")
- key = Key::RightArrow;
- else if (event.keyIdentifier() == "Up")
- key = Key::UpArrow;
- else if (event.keyIdentifier() == "Down")
- key = Key::DownArrow;
- else if (event.charCode() == ' ')
- key = Key::Space;
- else
- return false;
+ if (animator)
+ animator->handleKeyUpEvent();
+}
- auto granularity = [&] {
- switch (key) {
- case Key::LeftArrow:
- case Key::RightArrow:
- return event.altKey() ? ScrollGranularity::ScrollByPage : ScrollGranularity::ScrollByLine;
- case Key::UpArrow:
- case Key::DownArrow:
- if (event.metaKey())
- return ScrollGranularity::ScrollByDocument;
- if (event.altKey())
- return ScrollGranularity::ScrollByPage;
- return ScrollGranularity::ScrollByLine;
- case Key::Space:
- return ScrollGranularity::ScrollByPage;
- };
- RELEASE_ASSERT_NOT_REACHED();
- }();
+bool EventHandler::startKeyboardScrolling(KeyboardEvent& event)
+{
+ Ref protectedFrame = m_frame;
+ FrameView* view = m_frame.view();
- auto direction = [&] {
- switch (key) {
- case Key::LeftArrow:
- return ScrollDirection::ScrollLeft;
- case Key::RightArrow:
- return ScrollDirection::ScrollRight;
- case Key::UpArrow:
- return ScrollDirection::ScrollUp;
- case Key::DownArrow:
- return ScrollDirection::ScrollDown;
- case Key::Space:
- return event.shiftKey() ? ScrollDirection::ScrollUp : ScrollDirection::ScrollDown;
- }
- RELEASE_ASSERT_NOT_REACHED();
- }();
+ KeyboardScrollingAnimator* animator = view->scrollAnimator().keyboardScrollingAnimator();
- return EventHandler::scrollRecursively(direction, granularity, nullptr);
+ if (animator)
+ return animator->beginKeyboardScrollGesture(event);
+
+ return false;
}
void EventHandler::defaultArrowEventHandler(FocusDirection focusDirection, KeyboardEvent& event)
@@ -4316,7 +4288,7 @@
if (!isSpatialNavigationEnabled(&m_frame)) {
if (m_frame.settings().eventHandlerDrivenSmoothKeyboardScrollingEnabled())
- handleKeyboardScrolling(event);
+ startKeyboardScrolling(event);
return;
}
Modified: trunk/Source/WebCore/page/EventHandler.h (280491 => 280492)
--- trunk/Source/WebCore/page/EventHandler.h 2021-07-30 20:25:46 UTC (rev 280491)
+++ trunk/Source/WebCore/page/EventHandler.h 2021-07-30 20:26:51 UTC (rev 280492)
@@ -377,7 +377,8 @@
bool handleMousePressEventTripleClick(const MouseEventWithHitTestResults&);
float scrollDistance(ScrollDirection, ScrollGranularity);
- bool handleKeyboardScrolling(KeyboardEvent&);
+ bool startKeyboardScrolling(KeyboardEvent&);
+ void stopKeyboardScrolling();
#if ENABLE(DRAG_SUPPORT)
bool handleMouseDraggedEvent(const MouseEventWithHitTestResults&, CheckDragHysteresis = ShouldCheckDragHysteresis);
Added: trunk/Source/WebCore/platform/KeyboardScrollingAnimator.cpp (0 => 280492)
--- trunk/Source/WebCore/platform/KeyboardScrollingAnimator.cpp (rev 0)
+++ trunk/Source/WebCore/platform/KeyboardScrollingAnimator.cpp 2021-07-30 20:26:51 UTC (rev 280492)
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "KeyboardScrollingAnimator.h"
+
+#include "EventNames.h"
+#include "ScrollTypes.h"
+#include "ScrollableArea.h"
+#include "WritingMode.h"
+
+namespace WebCore {
+
+KeyboardScrollingAnimator::KeyboardScrollingAnimator(ScrollAnimator& scrollAnimator, ScrollController& scrollController)
+ : m_scrollAnimator(scrollAnimator)
+ , m_scrollController(scrollController)
+{
+}
+
+RectEdges<bool> KeyboardScrollingAnimator::scrollableDirectionsFromOffset(FloatPoint offset) const
+{
+ auto minimumScrollPosition = m_scrollAnimator.scrollableArea().minimumScrollPosition();
+ auto maximumScrollPosition = m_scrollAnimator.scrollableArea().maximumScrollPosition();
+
+ RectEdges<bool> edges;
+
+ edges.setTop(offset.y() > minimumScrollPosition.y());
+ edges.setBottom(offset.y() < maximumScrollPosition.y());
+ edges.setLeft(offset.x() > minimumScrollPosition.x());
+ edges.setRight(offset.x() < maximumScrollPosition.x());
+
+ return edges;
+}
+
+static BoxSide boxSideForDirection(ScrollDirection direction)
+{
+ switch (direction) {
+ case ScrollDirection::ScrollUp:
+ return BoxSide::Top;
+ case ScrollDirection::ScrollDown:
+ return BoxSide::Bottom;
+ case ScrollDirection::ScrollLeft:
+ return BoxSide::Left;
+ case ScrollDirection::ScrollRight:
+ return BoxSide::Right;
+ }
+ ASSERT_NOT_REACHED();
+ return BoxSide::Top;
+}
+
+static FloatSize perpendicularAbsoluteUnitVector(ScrollDirection direction)
+{
+ switch (direction) {
+ case ScrollDirection::ScrollUp:
+ case ScrollDirection::ScrollDown:
+ return { 1, 0 };
+ case ScrollDirection::ScrollLeft:
+ case ScrollDirection::ScrollRight:
+ return { 0, 1 };
+ }
+ ASSERT_NOT_REACHED();
+ return { };
+}
+
+void KeyboardScrollingAnimator::updateKeyboardScrollPosition(MonotonicTime currentTime)
+{
+ auto force = FloatSize { };
+ auto axesToApplySpring = FloatSize { 1, 1 };
+ KeyboardScrollParameters params = KeyboardScrollParameters::parameters();
+
+ if (m_currentKeyboardScroll) {
+ auto scrollableDirections = scrollableDirectionsFromOffset(m_scrollAnimator.currentPosition());
+ auto direction = m_currentKeyboardScroll->direction;
+
+ if (scrollableDirections.at(boxSideForDirection(direction))) {
+ // Apply the scrolling force. Only apply the spring in the perpendicular axis,
+ // otherwise it drags against the direction of motion.
+ axesToApplySpring = perpendicularAbsoluteUnitVector(direction);
+ force = m_currentKeyboardScroll->force;
+ } else {
+ // The scroll view cannot scroll in this direction, and is rubber-banding.
+ // Apply a constant and significant force; otherwise, the force for a
+ // single-line increment is not strong enough to rubber-band perceptibly.
+ force = unitVectorForScrollDirection(direction).scaled(params.rubberBandForce);
+ }
+
+ if (fabs(m_velocity.width()) >= fabs(m_currentKeyboardScroll->maximumVelocity.width()))
+ force.setWidth(0);
+
+ if (fabs(m_velocity.height()) >= fabs(m_currentKeyboardScroll->maximumVelocity.height()))
+ force.setHeight(0);
+ }
+
+ ScrollPosition idealPosition = m_scrollAnimator.scrollableArea().constrainScrollPosition(IntPoint(m_currentKeyboardScroll ? m_scrollAnimator.currentPosition() : m_idealPosition));
+ FloatSize displacement = m_scrollAnimator.currentPosition() - idealPosition;
+
+ auto springForce = -displacement.scaled(params.springStiffness) - m_velocity.scaled(params.springDamping);
+ force += springForce * axesToApplySpring;
+
+ float frameDuration = (currentTime - m_timeAtLastFrame).value();
+ m_timeAtLastFrame = currentTime;
+
+ FloatSize acceleration = force.scaled(1. / params.springMass);
+ m_velocity += acceleration.scaled(frameDuration);
+ FloatPoint newPosition = m_scrollAnimator.currentPosition() + m_velocity.scaled(frameDuration);
+
+ m_scrollAnimator.scrollToPositionWithoutAnimation(newPosition);
+
+ if (!m_scrollTriggeringKeyIsPressed && m_velocity.diagonalLengthSquared() < 1) {
+ m_scrollController.stopKeyboardScrolling();
+ m_velocity = { };
+ }
+}
+
+float KeyboardScrollingAnimator::scrollDistance(ScrollDirection direction, ScrollGranularity granularity) const
+{
+ auto scrollbar = [&] {
+ if (direction == ScrollDirection::ScrollUp || direction == ScrollDirection::ScrollDown)
+ return m_scrollAnimator.scrollableArea().verticalScrollbar();
+ return m_scrollAnimator.scrollableArea().horizontalScrollbar();
+ }();
+
+ switch (granularity) {
+ case ScrollGranularity::ScrollByLine:
+ return scrollbar->lineStep();
+ case ScrollGranularity::ScrollByPage:
+ return scrollbar->pageStep();
+ case ScrollGranularity::ScrollByDocument:
+ return scrollbar->totalSize();
+ case ScrollGranularity::ScrollByPixel:
+ return scrollbar->pixelStep();
+ }
+
+ return 0;
+}
+
+std::optional<KeyboardScroll> KeyboardScrollingAnimator::keyboardScrollForKeyboardEvent(KeyboardEvent& event) const
+{
+ // FIXME (bug 227459): This logic does not account for writing-mode.
+
+ enum class Key : uint8_t { LeftArrow, RightArrow, UpArrow, DownArrow, Space };
+
+ Key key;
+ if (event.keyIdentifier() == "Left")
+ key = Key::LeftArrow;
+ else if (event.keyIdentifier() == "Right")
+ key = Key::RightArrow;
+ else if (event.keyIdentifier() == "Up")
+ key = Key::UpArrow;
+ else if (event.keyIdentifier() == "Down")
+ key = Key::DownArrow;
+ else if (event.charCode() == ' ')
+ key = Key::Space;
+ else
+ return std::nullopt;
+
+ auto granularity = [&] {
+ switch (key) {
+ case Key::LeftArrow:
+ case Key::RightArrow:
+ return event.altKey() ? ScrollGranularity::ScrollByPage : ScrollGranularity::ScrollByLine;
+ case Key::UpArrow:
+ case Key::DownArrow:
+ if (event.metaKey())
+ return ScrollGranularity::ScrollByDocument;
+ if (event.altKey())
+ return ScrollGranularity::ScrollByPage;
+ return ScrollGranularity::ScrollByLine;
+ case Key::Space:
+ return ScrollGranularity::ScrollByPage;
+ };
+ }();
+
+ auto direction = [&] {
+ switch (key) {
+ case Key::LeftArrow:
+ return ScrollDirection::ScrollLeft;
+ case Key::RightArrow:
+ return ScrollDirection::ScrollRight;
+ case Key::UpArrow:
+ return ScrollDirection::ScrollUp;
+ case Key::DownArrow:
+ return ScrollDirection::ScrollDown;
+ case Key::Space:
+ return event.shiftKey() ? ScrollDirection::ScrollUp : ScrollDirection::ScrollDown;
+ }
+ }();
+
+ float distance = scrollDistance(direction, granularity);
+
+ KeyboardScroll scroll;
+
+ scroll.offset = unitVectorForScrollDirection(direction).scaled(distance);
+ scroll.granularity = granularity;
+ scroll.direction = direction;
+ scroll.maximumVelocity = scroll.offset.scaled(KeyboardScrollParameters::parameters().maximumVelocityMultiplier);
+ scroll.force = scroll.maximumVelocity.scaled(KeyboardScrollParameters::parameters().springMass / KeyboardScrollParameters::parameters().timeToMaximumVelocity);
+
+ return scroll;
+}
+
+bool KeyboardScrollingAnimator::beginKeyboardScrollGesture(KeyboardEvent& event)
+{
+ auto scroll = keyboardScrollForKeyboardEvent(event);
+
+ if (!scroll)
+ return false;
+
+ m_currentKeyboardScroll = scroll;
+
+ if (event.type() != eventNames().keydownEvent)
+ return false;
+
+ if (m_scrollTriggeringKeyIsPressed)
+ return false;
+
+ if (m_currentKeyboardScroll->granularity == ScrollGranularity::ScrollByDocument) {
+ m_velocity = { };
+ stopKeyboardScrollAnimation();
+ auto newPosition = IntPoint(m_scrollAnimator.currentPosition() + m_currentKeyboardScroll->offset);
+ m_scrollAnimator.scrollToPositionWithAnimation(newPosition);
+ return true;
+ }
+
+ m_timeAtLastFrame = MonotonicTime::now();
+ m_scrollTriggeringKeyIsPressed = true;
+
+ m_idealPositionForMinimumTravel = m_scrollAnimator.currentPosition() + m_currentKeyboardScroll->offset;
+ m_scrollController.beginKeyboardScrolling();
+
+ return true;
+}
+
+static ScrollPosition farthestPointInDirection(FloatPoint a, FloatPoint b, ScrollDirection direction)
+{
+ switch (direction) {
+ case ScrollDirection::ScrollUp:
+ return ScrollPosition(a.x(), std::min(a.y(), b.y()));
+ case ScrollDirection::ScrollDown:
+ return ScrollPosition(a.x(), std::max(a.y(), b.y()));
+ case ScrollDirection::ScrollLeft:
+ return ScrollPosition(std::min(a.x(), b.x()), a.y());
+ case ScrollDirection::ScrollRight:
+ return ScrollPosition(std::max(a.x(), b.x()), a.y());
+ }
+
+ ASSERT_NOT_REACHED();
+ return { };
+}
+
+void KeyboardScrollingAnimator::stopKeyboardScrollAnimation()
+{
+ if (!m_currentKeyboardScroll)
+ return;
+
+ auto params = KeyboardScrollParameters::parameters();
+
+ // Determine the settling position of the spring, conserving the system's current energy.
+ // Kinetic = elastic potential
+ // 1/2 * m * v^2 = 1/2 * k * x^2
+ // x = sqrt(v^2 * m / k)
+ auto displacementMagnitudeSquared = (m_velocity * m_velocity).scaled(params.springMass / params.springStiffness);
+ FloatSize displacement = {
+ std::copysign(sqrt(displacementMagnitudeSquared.width()), m_velocity.width()),
+ std::copysign(sqrt(displacementMagnitudeSquared.height()), m_velocity.height())
+ };
+
+ // If the spring would settle before the minimum travel distance
+ // for an instantaneous tap, move the settling position of the spring
+ // out to that point.
+ ScrollPosition farthestPoint = farthestPointInDirection(m_scrollAnimator.currentPosition() + displacement, m_idealPositionForMinimumTravel, m_currentKeyboardScroll->direction);
+ m_idealPosition = m_scrollAnimator.scrollableArea().constrainScrollPosition(farthestPoint);
+
+ m_currentKeyboardScroll = std::nullopt;
+}
+
+void KeyboardScrollingAnimator::handleKeyUpEvent()
+{
+ if (!m_scrollTriggeringKeyIsPressed)
+ return;
+
+ stopKeyboardScrollAnimation();
+ m_scrollTriggeringKeyIsPressed = false;
+}
+
+} // namespace WebCore
Added: trunk/Source/WebCore/platform/KeyboardScrollingAnimator.h (0 => 280492)
--- trunk/Source/WebCore/platform/KeyboardScrollingAnimator.h (rev 0)
+++ trunk/Source/WebCore/platform/KeyboardScrollingAnimator.h 2021-07-30 20:26:51 UTC (rev 280492)
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "KeyboardEvent.h"
+#include "KeyboardScroll.h"
+#include "RectEdges.h"
+#include "ScrollAnimator.h"
+
+namespace WebCore {
+
+class KeyboardScrollingAnimator {
+ WTF_MAKE_NONCOPYABLE(KeyboardScrollingAnimator);
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ KeyboardScrollingAnimator(ScrollAnimator&, ScrollController&);
+
+ bool beginKeyboardScrollGesture(KeyboardEvent&);
+ void handleKeyUpEvent();
+ void updateKeyboardScrollPosition(MonotonicTime);
+
+private:
+ void stopKeyboardScrollAnimation();
+ RectEdges<bool> scrollableDirectionsFromOffset(FloatPoint) const;
+ std::optional<KeyboardScroll> keyboardScrollForKeyboardEvent(KeyboardEvent&) const;
+ float scrollDistance(ScrollDirection, ScrollGranularity) const;
+
+ ScrollAnimator& m_scrollAnimator;
+ ScrollController& m_scrollController;
+ std::optional<WebCore::KeyboardScroll> m_currentKeyboardScroll;
+ bool m_scrollTriggeringKeyIsPressed;
+ FloatSize m_velocity;
+ MonotonicTime m_timeAtLastFrame;
+ FloatPoint m_idealPositionForMinimumTravel;
+ FloatPoint m_idealPosition;
+};
+
+} // namespace WebCore
Modified: trunk/Source/WebCore/platform/ScrollAnimator.cpp (280491 => 280492)
--- trunk/Source/WebCore/platform/ScrollAnimator.cpp 2021-07-30 20:25:46 UTC (rev 280491)
+++ trunk/Source/WebCore/platform/ScrollAnimator.cpp 2021-07-30 20:26:51 UTC (rev 280492)
@@ -33,6 +33,7 @@
#include "ScrollAnimator.h"
#include "FloatPoint.h"
+#include "KeyboardScrollingAnimator.h"
#include "LayoutSize.h"
#include "PlatformWheelEvent.h"
#include "ScrollAnimationSmooth.h"
@@ -67,6 +68,7 @@
[this] {
m_scrollableArea.setScrollBehaviorStatus(ScrollBehaviorStatus::NotInAnimation);
}))
+ , m_keyboardScrollingAnimator(makeUnique<KeyboardScrollingAnimator>(*this, m_scrollController))
{
}
@@ -332,7 +334,7 @@
{
if (m_scrollControllerAnimationTimer.isActive())
return;
-
+
m_scrollControllerAnimationTimer.startRepeating(1_s / 60.);
}
Modified: trunk/Source/WebCore/platform/ScrollAnimator.h (280491 => 280492)
--- trunk/Source/WebCore/platform/ScrollAnimator.h 2021-07-30 20:25:46 UTC (rev 280491)
+++ trunk/Source/WebCore/platform/ScrollAnimator.h 2021-07-30 20:26:51 UTC (rev 280492)
@@ -34,7 +34,6 @@
#include "FloatPoint.h"
#include "PlatformWheelEvent.h"
#include "ScrollController.h"
-#include "ScrollTypes.h"
#include "Timer.h"
#include "WheelEventTestMonitor.h"
#include <wtf/FastMalloc.h>
@@ -43,6 +42,7 @@
namespace WebCore {
class FloatPoint;
+class KeyboardScrollingAnimator;
class PlatformTouchEvent;
class ScrollAnimation;
class ScrollableArea;
@@ -81,6 +81,8 @@
virtual bool handleWheelEvent(const PlatformWheelEvent&);
+ KeyboardScrollingAnimator *keyboardScrollingAnimator() const override { return m_keyboardScrollingAnimator.get(); }
+
#if ENABLE(TOUCH_EVENTS)
virtual bool handleTouchEvent(const PlatformTouchEvent&);
#endif
@@ -178,6 +180,7 @@
FloatPoint m_currentPosition;
std::unique_ptr<ScrollAnimation> m_scrollAnimation;
+ std::unique_ptr<KeyboardScrollingAnimator> m_keyboardScrollingAnimator;
};
} // namespace WebCore
Modified: trunk/Source/WebCore/platform/ScrollController.cpp (280491 => 280492)
--- trunk/Source/WebCore/platform/ScrollController.cpp 2021-07-30 20:25:46 UTC (rev 280491)
+++ trunk/Source/WebCore/platform/ScrollController.cpp 2021-07-30 20:26:51 UTC (rev 280492)
@@ -26,6 +26,7 @@
#include "config.h"
#include "ScrollController.h"
+#include "KeyboardScrollingAnimator.h"
#include "LayoutSize.h"
#include "Logging.h"
#include "PlatformWheelEvent.h"
@@ -43,15 +44,16 @@
void ScrollController::animationCallback(MonotonicTime currentTime)
{
- LOG_WITH_STREAM(Scrolling, stream << "ScrollController " << this << " animationCallback: isAnimatingRubberBand " << m_isAnimatingRubberBand << " isAnimatingScrollSnap " << m_isAnimatingScrollSnap);
+ LOG_WITH_STREAM(Scrolling, stream << "ScrollController " << this << " animationCallback: isAnimatingRubberBand " << m_isAnimatingRubberBand << " isAnimatingScrollSnap " << m_isAnimatingScrollSnap << "isAnimatingKeyboardScrolling" << m_isAnimatingKeyboardScrolling);
updateScrollSnapAnimatingState(currentTime);
updateRubberBandAnimatingState(currentTime);
+ updateKeyboardScrollingAnimatingState(currentTime);
}
void ScrollController::startOrStopAnimationCallbacks()
{
- bool needsCallbacks = m_isAnimatingRubberBand || m_isAnimatingScrollSnap;
+ bool needsCallbacks = m_isAnimatingRubberBand || m_isAnimatingScrollSnap || m_isAnimatingKeyboardScrolling;
if (needsCallbacks == m_isRunningAnimatingCallback)
return;
@@ -65,6 +67,16 @@
m_isRunningAnimatingCallback = false;
}
+void ScrollController::beginKeyboardScrolling()
+{
+ setIsAnimatingKeyboardScrolling(true);
+}
+
+void ScrollController::stopKeyboardScrolling()
+{
+ setIsAnimatingKeyboardScrolling(false);
+}
+
void ScrollController::setIsAnimatingRubberBand(bool isAnimatingRubberBand)
{
if (isAnimatingRubberBand == m_isAnimatingRubberBand)
@@ -83,6 +95,15 @@
startOrStopAnimationCallbacks();
}
+void ScrollController::setIsAnimatingKeyboardScrolling(bool isAnimatingKeyboardScrolling)
+{
+ if (isAnimatingKeyboardScrolling == m_isAnimatingKeyboardScrolling)
+ return;
+
+ m_isAnimatingKeyboardScrolling = isAnimatingKeyboardScrolling;
+ startOrStopAnimationCallbacks();
+}
+
bool ScrollController::usesScrollSnap() const
{
return !!m_scrollSnapState;
@@ -198,6 +219,15 @@
setNearestScrollSnapIndexForAxisAndOffset(ScrollEventAxis::Vertical, offset);
}
+
+void ScrollController::updateKeyboardScrollingAnimatingState(MonotonicTime currentTime)
+{
+ if (!m_isAnimatingKeyboardScrolling)
+ return;
+
+ m_client.keyboardScrollingAnimator()->updateKeyboardScrollPosition(currentTime);
+}
+
// Currently, only Mac supports momentum srolling-based scrollsnapping and rubber banding
// so all of these methods are a noop on non-Mac platforms.
#if !PLATFORM(MAC)
Modified: trunk/Source/WebCore/platform/ScrollController.h (280491 => 280492)
--- trunk/Source/WebCore/platform/ScrollController.h 2021-07-30 20:25:46 UTC (rev 280491)
+++ trunk/Source/WebCore/platform/ScrollController.h 2021-07-30 20:26:51 UTC (rev 280492)
@@ -38,6 +38,7 @@
namespace WebCore {
+class KeyboardScrollingAnimator;
class LayoutSize;
class PlatformWheelEvent;
class ScrollController;
@@ -72,6 +73,9 @@
virtual void startAnimationCallback(ScrollController&) = 0;
virtual void stopAnimationCallback(ScrollController&) = 0;
+ virtual void updateKeyboardScrollPosition(MonotonicTime) { }
+ virtual KeyboardScrollingAnimator *keyboardScrollingAnimator() const { return nullptr; }
+
#if ENABLE(RUBBER_BANDING)
virtual bool allowsHorizontalStretching(const PlatformWheelEvent&) const = 0;
virtual bool allowsVerticalStretching(const PlatformWheelEvent&) const = 0;
@@ -121,6 +125,9 @@
bool usesScrollSnap() const;
void stopAllTimers();
void scrollPositionChanged();
+
+ void beginKeyboardScrolling();
+ void stopKeyboardScrolling();
// Should be called periodically by the client. Started by startAnimationCallback(), stopped by stopAnimationCallback().
void animationCallback(MonotonicTime);
@@ -163,9 +170,11 @@
void updateScrollSnapAnimatingState(MonotonicTime);
void updateRubberBandAnimatingState(MonotonicTime);
-
+ void updateKeyboardScrollingAnimatingState(MonotonicTime);
+
void setIsAnimatingRubberBand(bool);
void setIsAnimatingScrollSnap(bool);
+ void setIsAnimatingKeyboardScrolling(bool);
#if PLATFORM(MAC)
void startScrollSnapAnimation();
@@ -199,6 +208,7 @@
bool m_isRunningAnimatingCallback { false };
bool m_isAnimatingRubberBand { false };
bool m_isAnimatingScrollSnap { false };
+ bool m_isAnimatingKeyboardScrolling { false };
#if PLATFORM(MAC)
WallTime m_lastMomentumScrollTimestamp;