Diff
Modified: branches/safari-612.4.2.1-branch/Source/WTF/ChangeLog (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WTF/ChangeLog 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WTF/ChangeLog 2021-12-08 21:25:37 UTC (rev 286717)
@@ -1,3 +1,192 @@
+2021-12-01 Alan Coon <alanc...@apple.com>
+
+ Cherry-pick r286346. rdar://problem/85928816
+
+ Add a momentum event synthesizer
+ https://bugs.webkit.org/show_bug.cgi?id=233653
+ <rdar://problem/85571258>
+
+ Reviewed by Simon Fraser.
+
+ Source/WebCore/PAL:
+
+ * pal/spi/mac/IOKitSPIMac.h:
+ Add some SPI.
+
+ Source/WebKit:
+
+ * Platform/Logging.h:
+ Add ScrollAnimations log channel to WebKit (it already exists in WebCore).
+
+ * Shared/ScrollingAccelerationCurve.cpp: Added.
+ (WebKit::ScrollingAccelerationCurve::ScrollingAccelerationCurve):
+ (WebKit::ScrollingAccelerationCurve::interpolate):
+ (WebKit::ScrollingAccelerationCurve::computeIntermediateValuesIfNeeded):
+ (WebKit::ScrollingAccelerationCurve::evaluateQuartic):
+ (WebKit::ScrollingAccelerationCurve::accelerationFactor):
+ (WebKit::ScrollingAccelerationCurve::encode const):
+ (WebKit::ScrollingAccelerationCurve::decode):
+ (WebKit::operator<<):
+ (WebKit::ScrollingAccelerationCurve::fromNativeWheelEvent):
+ * Shared/ScrollingAccelerationCurve.h: Added.
+ (WebKit::ScrollingAccelerationCurve::operator== const):
+ (WebKit::ScrollingAccelerationCurve::operator!= const):
+ Add a class that represents a quartic scrolling acceleration curve with
+ a trailing linear tangent region, and allows interpolation between curves
+ and evaluation of the curve at a point.
+
+ * Shared/mac/ScrollingAccelerationCurveMac.mm: Added.
+ (WebKit::fromFixedPoint):
+ (WebKit::readFixedPointParameter):
+ (WebKit::fromIOHIDCurve):
+ (WebKit::fromIOHIDCurveArrayWithAcceleration):
+ (WebKit::ScrollingAccelerationCurve::fromNativeWheelEvent):
+ Given a NativeWebWheelEvent, extract its underlying platform event
+ and fetch the ScrollingAccelerationCurve for the originating device.
+
+ * Sources.txt:
+ * SourcesCocoa.txt:
+ * UIProcess/WebPageProxy.cpp:
+ (WebKit::WebPageProxy::handleWheelEvent):
+ Fetch the current ScrollingAccelerationCurve on each momentum begin event,
+ if the feature is enabled.
+
+ (WebKit::WebPageProxy::sendWheelEvent):
+ Send the ScrollingAccelerationCurve to EventDispatcher just before
+ we send the momentum begin event along, if it has changed.
+
+ (WebKit::WebPageProxy::windowScreenDidChange):
+ Keep EventDispatcher apprised of changes to the current screen of the page.
+
+ (WebKit::WebPageProxy::resetState):
+ * UIProcess/WebPageProxy.h:
+ * WebKit.xcodeproj/project.pbxproj:
+ * WebProcess/WebPage/EventDispatcher.cpp:
+ (WebKit::EventDispatcher::EventDispatcher):
+ (WebKit::EventDispatcher::internalWheelEvent):
+ Factor most of wheelEvent out into internalWheelEvent, so that
+ MomentumEventDispatcher can call internalWheelEvent(), skipping the code
+ in wheelEvent() that forwards the event... to MomentumEventDispatcher.
+
+ Also, keep track of where the event came from (UI process or MomentumEventDispatcher)
+ and avoid sending didReceiveEvent replies to the UI process for synthetic
+ events that it doesn't know about.
+
+ (WebKit::EventDispatcher::wheelEvent):
+ Forward wheel events to MomentumEventDispatcher.
+
+ (WebKit::EventDispatcher::setScrollingAccelerationCurve):
+ Forward scrolling curve changes to MomentumEventDispatcher.
+
+ (WebKit::EventDispatcher::displayWasRefreshed):
+ Forward display refresh callbacks to MomentumEventDispatcher.
+
+ (WebKit::EventDispatcher::windowScreenDidChange):
+ Forward page screen changes to MomentumEventDispatcher.
+
+ * WebProcess/WebPage/EventDispatcher.h:
+ * WebProcess/WebPage/EventDispatcher.messages.in:
+
+ * WebProcess/WebPage/WebPage.cpp:
+ (WebKit::WebPage::wheelEvent):
+ (WebKit::WebPage::dispatchWheelEventWithoutScrolling):
+ * WebProcess/WebPage/WebPage.h:
+ Don't send didReceiveEvent for synthetic events from the main thread either.
+
+ * WebProcess/WebPage/MomentumEventDispatcher.cpp: Added.
+ (WebKit::MomentumEventDispatcher::MomentumEventDispatcher):
+ (WebKit::MomentumEventDispatcher::~MomentumEventDispatcher):
+ (WebKit::MomentumEventDispatcher::eventShouldStartSyntheticMomentumPhase const):
+ If we have everything we need (a scrolling curve - which also implies
+ that the setting is enabled - and raw platform deltas), and the event
+ is a momentum begin event, we can use it to start a synthetic momentum phase.
+
+ (WebKit::MomentumEventDispatcher::handleWheelEvent):
+ Start or stop the momentum phase as appropriate.
+ Record any incoming fingers-down phase events in a rolling event history structure.
+ Block platform-event-driven events from being further handled by WebKit
+ if we are inside a synthetic momentum phase, since we'll be replacing them.
+
+ (WebKit::appKitScrollMultiplierForEvent):
+ Determine the AppKit scroll muliplier ("accelerated" vs "unaccelerated"
+ deltas in NSEvent parlance) from the event via division.
+
+ (WebKit::MomentumEventDispatcher::dispatchSyntheticMomentumEvent):
+ Dispatch a synthetic event with the given momentum phase and delta.
+ We borrow some properties from the event that initiated the momentum
+ phase (e.g. the position), and some from whatever event we've most
+ recently seen (even if we told the rest of WebKit to ignore it), so
+ that we get the most up-to-date state for e.g. the keyboard modifiers.
+
+ (WebKit::MomentumEventDispatcher::didStartMomentumPhase):
+ Record relevant information about the start event, generate the
+ momentum curve, and start a full-rate display link. Also, send a momentum
+ begin event, since we blocked the real one.
+
+ (WebKit::MomentumEventDispatcher::didEndMomentumPhase):
+ Send a momentum ended event, shut down the display link, and reset all
+ per-gesture state.
+
+ (WebKit::MomentumEventDispatcher::setScrollingAccelerationCurve):
+ Store ScrollingAccelerationCurves on a per-page basis.
+
+ (WebKit::MomentumEventDispatcher::displayID const):
+ Look up the displayID for the current gesture's page.
+
+ (WebKit::MomentumEventDispatcher::startDisplayLink):
+ (WebKit::MomentumEventDispatcher::stopDisplayLink):
+ Start and stop a display link, which will - in a roundabout manner -
+ eventually call back into displayWasRefreshed.
+
+ (WebKit::MomentumEventDispatcher::windowScreenDidChange):
+ Store DisplayIDs on a per-page basis.
+
+ (WebKit::MomentumEventDispatcher::displayWasRefreshed):
+ When the display link calls us back, emit a single momentum changed
+ event of sufficient delta to move us to the desired offset along the curve.
+
+ (WebKit::MomentumEventDispatcher::didScrollWithInterval):
+ (WebKit::MomentumEventDispatcher::didScroll):
+ Record incoming scrolling events into a rolling history deque.
+
+ (WebKit::MomentumEventDispatcher::buildOffsetTableWithInitialDelta):
+ Compute the offsets for the entire "ideal curve" table (at 60Hz) by
+ running the animation and accumulating the deltas.
+
+ (WebKit::interpolate):
+ (WebKit::MomentumEventDispatcher::offsetAtTime):
+ Interpolate along the "ideal curve" to determine the intended offset
+ at a given time.
+
+ (WebKit::momentumDecayRate):
+ (WebKit::MomentumEventDispatcher::computeNextDelta):
+ Compute the next delta given a delta (and the event history) by applying
+ a bit of exponential decay and then passing it through the
+ ScrollingAccelerationCurve. We return both the unaccelerated
+ and accelerated deltas, because the input to the next iteration of
+ computeNextDelta is the unaccelerated delta, but the delta applied
+ to the actual scroll position is the accelerated delta.
+
+ * WebProcess/WebPage/MomentumEventDispatcher.h: Added.
+
+ Source/WTF:
+
+ * wtf/PlatformEnable.h:
+ Add an ENABLE().
+
+ git-svn-id: https://svn.webkit.org/repository/webkit/trunk@286346 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+ 2021-11-30 Tim Horton <timothy_hor...@apple.com>
+
+ Add a momentum event synthesizer
+ https://bugs.webkit.org/show_bug.cgi?id=233653
+ <rdar://problem/85571258>
+
+ Reviewed by Simon Fraser.
+
+ * wtf/PlatformEnable.h:
+ Add an ENABLE().
+
2021-11-18 Russell Epstein <repst...@apple.com>
Cherry-pick r285130. rdar://problem/85166839
Modified: branches/safari-612.4.2.1-branch/Source/WTF/wtf/PlatformEnable.h (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WTF/wtf/PlatformEnable.h 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WTF/wtf/PlatformEnable.h 2021-12-08 21:25:37 UTC (rev 286717)
@@ -868,6 +868,10 @@
#define ENABLE_WHEEL_EVENT_LATCHING 1
#endif
+#if PLATFORM(MAC)
+#define ENABLE_MOMENTUM_EVENT_DISPATCHER 1
+#endif
+
#if !defined(ENABLE_SCROLLING_THREAD)
#if USE(NICOSIA)
#define ENABLE_SCROLLING_THREAD 1
Modified: branches/safari-612.4.2.1-branch/Source/WebCore/PAL/ChangeLog (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebCore/PAL/ChangeLog 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WebCore/PAL/ChangeLog 2021-12-08 21:25:37 UTC (rev 286717)
@@ -1,5 +1,194 @@
2021-12-01 Alan Coon <alanc...@apple.com>
+ Cherry-pick r286346. rdar://problem/85928816
+
+ Add a momentum event synthesizer
+ https://bugs.webkit.org/show_bug.cgi?id=233653
+ <rdar://problem/85571258>
+
+ Reviewed by Simon Fraser.
+
+ Source/WebCore/PAL:
+
+ * pal/spi/mac/IOKitSPIMac.h:
+ Add some SPI.
+
+ Source/WebKit:
+
+ * Platform/Logging.h:
+ Add ScrollAnimations log channel to WebKit (it already exists in WebCore).
+
+ * Shared/ScrollingAccelerationCurve.cpp: Added.
+ (WebKit::ScrollingAccelerationCurve::ScrollingAccelerationCurve):
+ (WebKit::ScrollingAccelerationCurve::interpolate):
+ (WebKit::ScrollingAccelerationCurve::computeIntermediateValuesIfNeeded):
+ (WebKit::ScrollingAccelerationCurve::evaluateQuartic):
+ (WebKit::ScrollingAccelerationCurve::accelerationFactor):
+ (WebKit::ScrollingAccelerationCurve::encode const):
+ (WebKit::ScrollingAccelerationCurve::decode):
+ (WebKit::operator<<):
+ (WebKit::ScrollingAccelerationCurve::fromNativeWheelEvent):
+ * Shared/ScrollingAccelerationCurve.h: Added.
+ (WebKit::ScrollingAccelerationCurve::operator== const):
+ (WebKit::ScrollingAccelerationCurve::operator!= const):
+ Add a class that represents a quartic scrolling acceleration curve with
+ a trailing linear tangent region, and allows interpolation between curves
+ and evaluation of the curve at a point.
+
+ * Shared/mac/ScrollingAccelerationCurveMac.mm: Added.
+ (WebKit::fromFixedPoint):
+ (WebKit::readFixedPointParameter):
+ (WebKit::fromIOHIDCurve):
+ (WebKit::fromIOHIDCurveArrayWithAcceleration):
+ (WebKit::ScrollingAccelerationCurve::fromNativeWheelEvent):
+ Given a NativeWebWheelEvent, extract its underlying platform event
+ and fetch the ScrollingAccelerationCurve for the originating device.
+
+ * Sources.txt:
+ * SourcesCocoa.txt:
+ * UIProcess/WebPageProxy.cpp:
+ (WebKit::WebPageProxy::handleWheelEvent):
+ Fetch the current ScrollingAccelerationCurve on each momentum begin event,
+ if the feature is enabled.
+
+ (WebKit::WebPageProxy::sendWheelEvent):
+ Send the ScrollingAccelerationCurve to EventDispatcher just before
+ we send the momentum begin event along, if it has changed.
+
+ (WebKit::WebPageProxy::windowScreenDidChange):
+ Keep EventDispatcher apprised of changes to the current screen of the page.
+
+ (WebKit::WebPageProxy::resetState):
+ * UIProcess/WebPageProxy.h:
+ * WebKit.xcodeproj/project.pbxproj:
+ * WebProcess/WebPage/EventDispatcher.cpp:
+ (WebKit::EventDispatcher::EventDispatcher):
+ (WebKit::EventDispatcher::internalWheelEvent):
+ Factor most of wheelEvent out into internalWheelEvent, so that
+ MomentumEventDispatcher can call internalWheelEvent(), skipping the code
+ in wheelEvent() that forwards the event... to MomentumEventDispatcher.
+
+ Also, keep track of where the event came from (UI process or MomentumEventDispatcher)
+ and avoid sending didReceiveEvent replies to the UI process for synthetic
+ events that it doesn't know about.
+
+ (WebKit::EventDispatcher::wheelEvent):
+ Forward wheel events to MomentumEventDispatcher.
+
+ (WebKit::EventDispatcher::setScrollingAccelerationCurve):
+ Forward scrolling curve changes to MomentumEventDispatcher.
+
+ (WebKit::EventDispatcher::displayWasRefreshed):
+ Forward display refresh callbacks to MomentumEventDispatcher.
+
+ (WebKit::EventDispatcher::windowScreenDidChange):
+ Forward page screen changes to MomentumEventDispatcher.
+
+ * WebProcess/WebPage/EventDispatcher.h:
+ * WebProcess/WebPage/EventDispatcher.messages.in:
+
+ * WebProcess/WebPage/WebPage.cpp:
+ (WebKit::WebPage::wheelEvent):
+ (WebKit::WebPage::dispatchWheelEventWithoutScrolling):
+ * WebProcess/WebPage/WebPage.h:
+ Don't send didReceiveEvent for synthetic events from the main thread either.
+
+ * WebProcess/WebPage/MomentumEventDispatcher.cpp: Added.
+ (WebKit::MomentumEventDispatcher::MomentumEventDispatcher):
+ (WebKit::MomentumEventDispatcher::~MomentumEventDispatcher):
+ (WebKit::MomentumEventDispatcher::eventShouldStartSyntheticMomentumPhase const):
+ If we have everything we need (a scrolling curve - which also implies
+ that the setting is enabled - and raw platform deltas), and the event
+ is a momentum begin event, we can use it to start a synthetic momentum phase.
+
+ (WebKit::MomentumEventDispatcher::handleWheelEvent):
+ Start or stop the momentum phase as appropriate.
+ Record any incoming fingers-down phase events in a rolling event history structure.
+ Block platform-event-driven events from being further handled by WebKit
+ if we are inside a synthetic momentum phase, since we'll be replacing them.
+
+ (WebKit::appKitScrollMultiplierForEvent):
+ Determine the AppKit scroll muliplier ("accelerated" vs "unaccelerated"
+ deltas in NSEvent parlance) from the event via division.
+
+ (WebKit::MomentumEventDispatcher::dispatchSyntheticMomentumEvent):
+ Dispatch a synthetic event with the given momentum phase and delta.
+ We borrow some properties from the event that initiated the momentum
+ phase (e.g. the position), and some from whatever event we've most
+ recently seen (even if we told the rest of WebKit to ignore it), so
+ that we get the most up-to-date state for e.g. the keyboard modifiers.
+
+ (WebKit::MomentumEventDispatcher::didStartMomentumPhase):
+ Record relevant information about the start event, generate the
+ momentum curve, and start a full-rate display link. Also, send a momentum
+ begin event, since we blocked the real one.
+
+ (WebKit::MomentumEventDispatcher::didEndMomentumPhase):
+ Send a momentum ended event, shut down the display link, and reset all
+ per-gesture state.
+
+ (WebKit::MomentumEventDispatcher::setScrollingAccelerationCurve):
+ Store ScrollingAccelerationCurves on a per-page basis.
+
+ (WebKit::MomentumEventDispatcher::displayID const):
+ Look up the displayID for the current gesture's page.
+
+ (WebKit::MomentumEventDispatcher::startDisplayLink):
+ (WebKit::MomentumEventDispatcher::stopDisplayLink):
+ Start and stop a display link, which will - in a roundabout manner -
+ eventually call back into displayWasRefreshed.
+
+ (WebKit::MomentumEventDispatcher::windowScreenDidChange):
+ Store DisplayIDs on a per-page basis.
+
+ (WebKit::MomentumEventDispatcher::displayWasRefreshed):
+ When the display link calls us back, emit a single momentum changed
+ event of sufficient delta to move us to the desired offset along the curve.
+
+ (WebKit::MomentumEventDispatcher::didScrollWithInterval):
+ (WebKit::MomentumEventDispatcher::didScroll):
+ Record incoming scrolling events into a rolling history deque.
+
+ (WebKit::MomentumEventDispatcher::buildOffsetTableWithInitialDelta):
+ Compute the offsets for the entire "ideal curve" table (at 60Hz) by
+ running the animation and accumulating the deltas.
+
+ (WebKit::interpolate):
+ (WebKit::MomentumEventDispatcher::offsetAtTime):
+ Interpolate along the "ideal curve" to determine the intended offset
+ at a given time.
+
+ (WebKit::momentumDecayRate):
+ (WebKit::MomentumEventDispatcher::computeNextDelta):
+ Compute the next delta given a delta (and the event history) by applying
+ a bit of exponential decay and then passing it through the
+ ScrollingAccelerationCurve. We return both the unaccelerated
+ and accelerated deltas, because the input to the next iteration of
+ computeNextDelta is the unaccelerated delta, but the delta applied
+ to the actual scroll position is the accelerated delta.
+
+ * WebProcess/WebPage/MomentumEventDispatcher.h: Added.
+
+ Source/WTF:
+
+ * wtf/PlatformEnable.h:
+ Add an ENABLE().
+
+ git-svn-id: https://svn.webkit.org/repository/webkit/trunk@286346 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+ 2021-11-30 Tim Horton <timothy_hor...@apple.com>
+
+ Add a momentum event synthesizer
+ https://bugs.webkit.org/show_bug.cgi?id=233653
+ <rdar://problem/85571258>
+
+ Reviewed by Simon Fraser.
+
+ * pal/spi/mac/IOKitSPIMac.h:
+ Add some SPI.
+
+2021-12-01 Alan Coon <alanc...@apple.com>
+
Cherry-pick r285790. rdar://problem/85928816
Attach IOHIDEvent timestamps to wheel events
Modified: branches/safari-612.4.2.1-branch/Source/WebCore/PAL/pal/spi/mac/IOKitSPIMac.h (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebCore/PAL/pal/spi/mac/IOKitSPIMac.h 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WebCore/PAL/pal/spi/mac/IOKitSPIMac.h 2021-12-08 21:25:37 UTC (rev 286717)
@@ -66,6 +66,8 @@
void IOHIDEventSystemClientRegisterDeviceMatchingBlock(IOHIDEventSystemClientRef, IOHIDServiceClientBlock, void *, void *);
void IOHIDEventSystemClientUnregisterDeviceMatchingBlock(IOHIDEventSystemClientRef);
void IOHIDEventSystemClientScheduleWithDispatchQueue(IOHIDEventSystemClientRef, dispatch_queue_t);
+void IOHIDEventSystemClientSetDispatchQueue(IOHIDEventSystemClientRef, dispatch_queue_t);
+void IOHIDEventSystemClientActivate(IOHIDEventSystemClientRef);
CFTypeRef IOHIDServiceClientCopyProperty(IOHIDServiceClientRef service, CFStringRef key);
@@ -84,6 +86,7 @@
typedef uint32_t IOHIDEventType;
typedef uint32_t IOHIDEventField;
+typedef uint64_t IOHIDEventSenderID;
#ifdef __LP64__
typedef double IOHIDFloat;
@@ -99,6 +102,7 @@
uint64_t IOHIDEventGetTimeStamp(IOHIDEventRef);
IOHIDFloat IOHIDEventGetFloatValue(IOHIDEventRef, IOHIDEventField);
+IOHIDEventSenderID IOHIDEventGetSenderID(IOHIDEventRef);
WTF_EXTERN_C_END
Modified: branches/safari-612.4.2.1-branch/Source/WebKit/ChangeLog (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/ChangeLog 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/ChangeLog 2021-12-08 21:25:37 UTC (rev 286717)
@@ -1,5 +1,345 @@
2021-12-01 Alan Coon <alanc...@apple.com>
+ Cherry-pick r286346. rdar://problem/85928816
+
+ Add a momentum event synthesizer
+ https://bugs.webkit.org/show_bug.cgi?id=233653
+ <rdar://problem/85571258>
+
+ Reviewed by Simon Fraser.
+
+ Source/WebCore/PAL:
+
+ * pal/spi/mac/IOKitSPIMac.h:
+ Add some SPI.
+
+ Source/WebKit:
+
+ * Platform/Logging.h:
+ Add ScrollAnimations log channel to WebKit (it already exists in WebCore).
+
+ * Shared/ScrollingAccelerationCurve.cpp: Added.
+ (WebKit::ScrollingAccelerationCurve::ScrollingAccelerationCurve):
+ (WebKit::ScrollingAccelerationCurve::interpolate):
+ (WebKit::ScrollingAccelerationCurve::computeIntermediateValuesIfNeeded):
+ (WebKit::ScrollingAccelerationCurve::evaluateQuartic):
+ (WebKit::ScrollingAccelerationCurve::accelerationFactor):
+ (WebKit::ScrollingAccelerationCurve::encode const):
+ (WebKit::ScrollingAccelerationCurve::decode):
+ (WebKit::operator<<):
+ (WebKit::ScrollingAccelerationCurve::fromNativeWheelEvent):
+ * Shared/ScrollingAccelerationCurve.h: Added.
+ (WebKit::ScrollingAccelerationCurve::operator== const):
+ (WebKit::ScrollingAccelerationCurve::operator!= const):
+ Add a class that represents a quartic scrolling acceleration curve with
+ a trailing linear tangent region, and allows interpolation between curves
+ and evaluation of the curve at a point.
+
+ * Shared/mac/ScrollingAccelerationCurveMac.mm: Added.
+ (WebKit::fromFixedPoint):
+ (WebKit::readFixedPointParameter):
+ (WebKit::fromIOHIDCurve):
+ (WebKit::fromIOHIDCurveArrayWithAcceleration):
+ (WebKit::ScrollingAccelerationCurve::fromNativeWheelEvent):
+ Given a NativeWebWheelEvent, extract its underlying platform event
+ and fetch the ScrollingAccelerationCurve for the originating device.
+
+ * Sources.txt:
+ * SourcesCocoa.txt:
+ * UIProcess/WebPageProxy.cpp:
+ (WebKit::WebPageProxy::handleWheelEvent):
+ Fetch the current ScrollingAccelerationCurve on each momentum begin event,
+ if the feature is enabled.
+
+ (WebKit::WebPageProxy::sendWheelEvent):
+ Send the ScrollingAccelerationCurve to EventDispatcher just before
+ we send the momentum begin event along, if it has changed.
+
+ (WebKit::WebPageProxy::windowScreenDidChange):
+ Keep EventDispatcher apprised of changes to the current screen of the page.
+
+ (WebKit::WebPageProxy::resetState):
+ * UIProcess/WebPageProxy.h:
+ * WebKit.xcodeproj/project.pbxproj:
+ * WebProcess/WebPage/EventDispatcher.cpp:
+ (WebKit::EventDispatcher::EventDispatcher):
+ (WebKit::EventDispatcher::internalWheelEvent):
+ Factor most of wheelEvent out into internalWheelEvent, so that
+ MomentumEventDispatcher can call internalWheelEvent(), skipping the code
+ in wheelEvent() that forwards the event... to MomentumEventDispatcher.
+
+ Also, keep track of where the event came from (UI process or MomentumEventDispatcher)
+ and avoid sending didReceiveEvent replies to the UI process for synthetic
+ events that it doesn't know about.
+
+ (WebKit::EventDispatcher::wheelEvent):
+ Forward wheel events to MomentumEventDispatcher.
+
+ (WebKit::EventDispatcher::setScrollingAccelerationCurve):
+ Forward scrolling curve changes to MomentumEventDispatcher.
+
+ (WebKit::EventDispatcher::displayWasRefreshed):
+ Forward display refresh callbacks to MomentumEventDispatcher.
+
+ (WebKit::EventDispatcher::windowScreenDidChange):
+ Forward page screen changes to MomentumEventDispatcher.
+
+ * WebProcess/WebPage/EventDispatcher.h:
+ * WebProcess/WebPage/EventDispatcher.messages.in:
+
+ * WebProcess/WebPage/WebPage.cpp:
+ (WebKit::WebPage::wheelEvent):
+ (WebKit::WebPage::dispatchWheelEventWithoutScrolling):
+ * WebProcess/WebPage/WebPage.h:
+ Don't send didReceiveEvent for synthetic events from the main thread either.
+
+ * WebProcess/WebPage/MomentumEventDispatcher.cpp: Added.
+ (WebKit::MomentumEventDispatcher::MomentumEventDispatcher):
+ (WebKit::MomentumEventDispatcher::~MomentumEventDispatcher):
+ (WebKit::MomentumEventDispatcher::eventShouldStartSyntheticMomentumPhase const):
+ If we have everything we need (a scrolling curve - which also implies
+ that the setting is enabled - and raw platform deltas), and the event
+ is a momentum begin event, we can use it to start a synthetic momentum phase.
+
+ (WebKit::MomentumEventDispatcher::handleWheelEvent):
+ Start or stop the momentum phase as appropriate.
+ Record any incoming fingers-down phase events in a rolling event history structure.
+ Block platform-event-driven events from being further handled by WebKit
+ if we are inside a synthetic momentum phase, since we'll be replacing them.
+
+ (WebKit::appKitScrollMultiplierForEvent):
+ Determine the AppKit scroll muliplier ("accelerated" vs "unaccelerated"
+ deltas in NSEvent parlance) from the event via division.
+
+ (WebKit::MomentumEventDispatcher::dispatchSyntheticMomentumEvent):
+ Dispatch a synthetic event with the given momentum phase and delta.
+ We borrow some properties from the event that initiated the momentum
+ phase (e.g. the position), and some from whatever event we've most
+ recently seen (even if we told the rest of WebKit to ignore it), so
+ that we get the most up-to-date state for e.g. the keyboard modifiers.
+
+ (WebKit::MomentumEventDispatcher::didStartMomentumPhase):
+ Record relevant information about the start event, generate the
+ momentum curve, and start a full-rate display link. Also, send a momentum
+ begin event, since we blocked the real one.
+
+ (WebKit::MomentumEventDispatcher::didEndMomentumPhase):
+ Send a momentum ended event, shut down the display link, and reset all
+ per-gesture state.
+
+ (WebKit::MomentumEventDispatcher::setScrollingAccelerationCurve):
+ Store ScrollingAccelerationCurves on a per-page basis.
+
+ (WebKit::MomentumEventDispatcher::displayID const):
+ Look up the displayID for the current gesture's page.
+
+ (WebKit::MomentumEventDispatcher::startDisplayLink):
+ (WebKit::MomentumEventDispatcher::stopDisplayLink):
+ Start and stop a display link, which will - in a roundabout manner -
+ eventually call back into displayWasRefreshed.
+
+ (WebKit::MomentumEventDispatcher::windowScreenDidChange):
+ Store DisplayIDs on a per-page basis.
+
+ (WebKit::MomentumEventDispatcher::displayWasRefreshed):
+ When the display link calls us back, emit a single momentum changed
+ event of sufficient delta to move us to the desired offset along the curve.
+
+ (WebKit::MomentumEventDispatcher::didScrollWithInterval):
+ (WebKit::MomentumEventDispatcher::didScroll):
+ Record incoming scrolling events into a rolling history deque.
+
+ (WebKit::MomentumEventDispatcher::buildOffsetTableWithInitialDelta):
+ Compute the offsets for the entire "ideal curve" table (at 60Hz) by
+ running the animation and accumulating the deltas.
+
+ (WebKit::interpolate):
+ (WebKit::MomentumEventDispatcher::offsetAtTime):
+ Interpolate along the "ideal curve" to determine the intended offset
+ at a given time.
+
+ (WebKit::momentumDecayRate):
+ (WebKit::MomentumEventDispatcher::computeNextDelta):
+ Compute the next delta given a delta (and the event history) by applying
+ a bit of exponential decay and then passing it through the
+ ScrollingAccelerationCurve. We return both the unaccelerated
+ and accelerated deltas, because the input to the next iteration of
+ computeNextDelta is the unaccelerated delta, but the delta applied
+ to the actual scroll position is the accelerated delta.
+
+ * WebProcess/WebPage/MomentumEventDispatcher.h: Added.
+
+ Source/WTF:
+
+ * wtf/PlatformEnable.h:
+ Add an ENABLE().
+
+ git-svn-id: https://svn.webkit.org/repository/webkit/trunk@286346 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+ 2021-11-30 Tim Horton <timothy_hor...@apple.com>
+
+ Add a momentum event synthesizer
+ https://bugs.webkit.org/show_bug.cgi?id=233653
+ <rdar://problem/85571258>
+
+ Reviewed by Simon Fraser.
+
+ * Platform/Logging.h:
+ Add ScrollAnimations log channel to WebKit (it already exists in WebCore).
+
+ * Shared/ScrollingAccelerationCurve.cpp: Added.
+ (WebKit::ScrollingAccelerationCurve::ScrollingAccelerationCurve):
+ (WebKit::ScrollingAccelerationCurve::interpolate):
+ (WebKit::ScrollingAccelerationCurve::computeIntermediateValuesIfNeeded):
+ (WebKit::ScrollingAccelerationCurve::evaluateQuartic):
+ (WebKit::ScrollingAccelerationCurve::accelerationFactor):
+ (WebKit::ScrollingAccelerationCurve::encode const):
+ (WebKit::ScrollingAccelerationCurve::decode):
+ (WebKit::operator<<):
+ (WebKit::ScrollingAccelerationCurve::fromNativeWheelEvent):
+ * Shared/ScrollingAccelerationCurve.h: Added.
+ (WebKit::ScrollingAccelerationCurve::operator== const):
+ (WebKit::ScrollingAccelerationCurve::operator!= const):
+ Add a class that represents a quartic scrolling acceleration curve with
+ a trailing linear tangent region, and allows interpolation between curves
+ and evaluation of the curve at a point.
+
+ * Shared/mac/ScrollingAccelerationCurveMac.mm: Added.
+ (WebKit::fromFixedPoint):
+ (WebKit::readFixedPointParameter):
+ (WebKit::fromIOHIDCurve):
+ (WebKit::fromIOHIDCurveArrayWithAcceleration):
+ (WebKit::ScrollingAccelerationCurve::fromNativeWheelEvent):
+ Given a NativeWebWheelEvent, extract its underlying platform event
+ and fetch the ScrollingAccelerationCurve for the originating device.
+
+ * Sources.txt:
+ * SourcesCocoa.txt:
+ * UIProcess/WebPageProxy.cpp:
+ (WebKit::WebPageProxy::handleWheelEvent):
+ Fetch the current ScrollingAccelerationCurve on each momentum begin event,
+ if the feature is enabled.
+
+ (WebKit::WebPageProxy::sendWheelEvent):
+ Send the ScrollingAccelerationCurve to EventDispatcher just before
+ we send the momentum begin event along, if it has changed.
+
+ (WebKit::WebPageProxy::windowScreenDidChange):
+ Keep EventDispatcher apprised of changes to the current screen of the page.
+
+ (WebKit::WebPageProxy::resetState):
+ * UIProcess/WebPageProxy.h:
+ * WebKit.xcodeproj/project.pbxproj:
+ * WebProcess/WebPage/EventDispatcher.cpp:
+ (WebKit::EventDispatcher::EventDispatcher):
+ (WebKit::EventDispatcher::internalWheelEvent):
+ Factor most of wheelEvent out into internalWheelEvent, so that
+ MomentumEventDispatcher can call internalWheelEvent(), skipping the code
+ in wheelEvent() that forwards the event... to MomentumEventDispatcher.
+
+ Also, keep track of where the event came from (UI process or MomentumEventDispatcher)
+ and avoid sending didReceiveEvent replies to the UI process for synthetic
+ events that it doesn't know about.
+
+ (WebKit::EventDispatcher::wheelEvent):
+ Forward wheel events to MomentumEventDispatcher.
+
+ (WebKit::EventDispatcher::setScrollingAccelerationCurve):
+ Forward scrolling curve changes to MomentumEventDispatcher.
+
+ (WebKit::EventDispatcher::displayWasRefreshed):
+ Forward display refresh callbacks to MomentumEventDispatcher.
+
+ (WebKit::EventDispatcher::windowScreenDidChange):
+ Forward page screen changes to MomentumEventDispatcher.
+
+ * WebProcess/WebPage/EventDispatcher.h:
+ * WebProcess/WebPage/EventDispatcher.messages.in:
+
+ * WebProcess/WebPage/WebPage.cpp:
+ (WebKit::WebPage::wheelEvent):
+ (WebKit::WebPage::dispatchWheelEventWithoutScrolling):
+ * WebProcess/WebPage/WebPage.h:
+ Don't send didReceiveEvent for synthetic events from the main thread either.
+
+ * WebProcess/WebPage/MomentumEventDispatcher.cpp: Added.
+ (WebKit::MomentumEventDispatcher::MomentumEventDispatcher):
+ (WebKit::MomentumEventDispatcher::~MomentumEventDispatcher):
+ (WebKit::MomentumEventDispatcher::eventShouldStartSyntheticMomentumPhase const):
+ If we have everything we need (a scrolling curve - which also implies
+ that the setting is enabled - and raw platform deltas), and the event
+ is a momentum begin event, we can use it to start a synthetic momentum phase.
+
+ (WebKit::MomentumEventDispatcher::handleWheelEvent):
+ Start or stop the momentum phase as appropriate.
+ Record any incoming fingers-down phase events in a rolling event history structure.
+ Block platform-event-driven events from being further handled by WebKit
+ if we are inside a synthetic momentum phase, since we'll be replacing them.
+
+ (WebKit::appKitScrollMultiplierForEvent):
+ Determine the AppKit scroll muliplier ("accelerated" vs "unaccelerated"
+ deltas in NSEvent parlance) from the event via division.
+
+ (WebKit::MomentumEventDispatcher::dispatchSyntheticMomentumEvent):
+ Dispatch a synthetic event with the given momentum phase and delta.
+ We borrow some properties from the event that initiated the momentum
+ phase (e.g. the position), and some from whatever event we've most
+ recently seen (even if we told the rest of WebKit to ignore it), so
+ that we get the most up-to-date state for e.g. the keyboard modifiers.
+
+ (WebKit::MomentumEventDispatcher::didStartMomentumPhase):
+ Record relevant information about the start event, generate the
+ momentum curve, and start a full-rate display link. Also, send a momentum
+ begin event, since we blocked the real one.
+
+ (WebKit::MomentumEventDispatcher::didEndMomentumPhase):
+ Send a momentum ended event, shut down the display link, and reset all
+ per-gesture state.
+
+ (WebKit::MomentumEventDispatcher::setScrollingAccelerationCurve):
+ Store ScrollingAccelerationCurves on a per-page basis.
+
+ (WebKit::MomentumEventDispatcher::displayID const):
+ Look up the displayID for the current gesture's page.
+
+ (WebKit::MomentumEventDispatcher::startDisplayLink):
+ (WebKit::MomentumEventDispatcher::stopDisplayLink):
+ Start and stop a display link, which will - in a roundabout manner -
+ eventually call back into displayWasRefreshed.
+
+ (WebKit::MomentumEventDispatcher::windowScreenDidChange):
+ Store DisplayIDs on a per-page basis.
+
+ (WebKit::MomentumEventDispatcher::displayWasRefreshed):
+ When the display link calls us back, emit a single momentum changed
+ event of sufficient delta to move us to the desired offset along the curve.
+
+ (WebKit::MomentumEventDispatcher::didScrollWithInterval):
+ (WebKit::MomentumEventDispatcher::didScroll):
+ Record incoming scrolling events into a rolling history deque.
+
+ (WebKit::MomentumEventDispatcher::buildOffsetTableWithInitialDelta):
+ Compute the offsets for the entire "ideal curve" table (at 60Hz) by
+ running the animation and accumulating the deltas.
+
+ (WebKit::interpolate):
+ (WebKit::MomentumEventDispatcher::offsetAtTime):
+ Interpolate along the "ideal curve" to determine the intended offset
+ at a given time.
+
+ (WebKit::momentumDecayRate):
+ (WebKit::MomentumEventDispatcher::computeNextDelta):
+ Compute the next delta given a delta (and the event history) by applying
+ a bit of exponential decay and then passing it through the
+ ScrollingAccelerationCurve. We return both the unaccelerated
+ and accelerated deltas, because the input to the next iteration of
+ computeNextDelta is the unaccelerated delta, but the delta applied
+ to the actual scroll position is the accelerated delta.
+
+ * WebProcess/WebPage/MomentumEventDispatcher.h: Added.
+
+2021-12-01 Alan Coon <alanc...@apple.com>
+
Cherry-pick r283353. rdar://problem/85928816
Simplify some scrolling-related code in WebKit with use of RectEdges<bool>
Modified: branches/safari-612.4.2.1-branch/Source/WebKit/Platform/Logging.h (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/Platform/Logging.h 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/Platform/Logging.h 2021-12-08 21:25:37 UTC (rev 286717)
@@ -89,6 +89,7 @@
M(RemoteLayerTree) \
M(Resize) \
M(ResourceLoadStatistics) \
+ M(ScrollAnimations) \
M(Scrolling) \
M(Selection) \
M(ServiceWorker) \
Added: branches/safari-612.4.2.1-branch/Source/WebKit/Shared/ScrollingAccelerationCurve.cpp (0 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/Shared/ScrollingAccelerationCurve.cpp (rev 0)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/Shared/ScrollingAccelerationCurve.cpp 2021-12-08 21:25:37 UTC (rev 286717)
@@ -0,0 +1,189 @@
+/*
+ * 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 "ScrollingAccelerationCurve.h"
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+
+#include "ArgumentCoders.h"
+#include "WebCoreArgumentCoders.h"
+#include <wtf/text/TextStream.h>
+
+namespace WebKit {
+
+ScrollingAccelerationCurve::ScrollingAccelerationCurve(float gainLinear, float gainParabolic, float gainCubic, float gainQuartic, float tangentSpeedLinear, float tangentSpeedParabolicRoot, float resolution)
+ : m_parameters { gainLinear, gainParabolic, gainCubic, gainQuartic, tangentSpeedLinear, tangentSpeedParabolicRoot, resolution }
+{
+}
+
+ScrollingAccelerationCurve ScrollingAccelerationCurve::interpolate(const ScrollingAccelerationCurve& from, const ScrollingAccelerationCurve& to, float amount)
+{
+ auto interpolate = [&] (float a, float b) -> float {
+ return a + amount * (b - a);
+ };
+
+ auto gainLinear = interpolate(from.m_parameters.gainLinear, to.m_parameters.gainLinear);
+ auto gainParabolic = interpolate(from.m_parameters.gainParabolic, to.m_parameters.gainParabolic);
+ auto gainCubic = interpolate(from.m_parameters.gainCubic, to.m_parameters.gainCubic);
+ auto gainQuartic = interpolate(from.m_parameters.gainQuartic, to.m_parameters.gainQuartic);
+
+ auto tangentSpeedLinear = interpolate(from.m_parameters.tangentSpeedLinear, to.m_parameters.tangentSpeedLinear);
+ auto tangentSpeedParabolicRoot = interpolate(from.m_parameters.tangentSpeedParabolicRoot, to.m_parameters.tangentSpeedParabolicRoot);
+
+ return { gainLinear, gainParabolic, gainCubic, gainQuartic, tangentSpeedLinear, tangentSpeedParabolicRoot, from.m_parameters.resolution };
+}
+
+void ScrollingAccelerationCurve::computeIntermediateValuesIfNeeded()
+{
+ if (m_intermediates)
+ return;
+ m_intermediates = ComputedIntermediateValues { };
+
+ float tangentInitialY;
+ m_intermediates->tangentStartX = std::numeric_limits<float>::max();
+ m_intermediates->falloffStartX = std::numeric_limits<float>::max();
+
+ auto quarticSlope = [&](float x) {
+ return (4 * std::pow(x, 3) * std::pow(m_parameters.gainQuartic, 4)) + (3 * std::pow(x, 2) * std::pow(m_parameters.gainCubic, 3)) + (2 * x * std::pow(m_parameters.gainParabolic, 2)) + m_parameters.gainLinear;
+ };
+
+ if (m_parameters.tangentSpeedLinear) {
+ tangentInitialY = evaluateQuartic(m_parameters.tangentSpeedLinear);
+ m_intermediates->tangentSlope = quarticSlope(m_parameters.tangentSpeedLinear);
+ m_intermediates->tangentIntercept = tangentInitialY - m_intermediates->tangentSlope * m_parameters.tangentSpeedLinear;
+ m_intermediates->tangentStartX = m_parameters.tangentSpeedLinear;
+
+ if (m_parameters.tangentSpeedParabolicRoot) {
+ float y1 = m_intermediates->tangentSlope * m_parameters.tangentSpeedParabolicRoot + m_intermediates->tangentIntercept;
+ m_intermediates->falloffSlope = 2 * y1 * m_intermediates->tangentSlope;
+ m_intermediates->falloffIntercept = std::pow(y1, 2) - m_intermediates->falloffSlope * m_parameters.tangentSpeedParabolicRoot;
+ m_intermediates->falloffStartX = m_parameters.tangentSpeedParabolicRoot;
+ }
+ } else if (m_parameters.tangentSpeedParabolicRoot) {
+ tangentInitialY = evaluateQuartic(m_parameters.tangentSpeedParabolicRoot);
+ m_intermediates->falloffSlope = quarticSlope(m_parameters.tangentSpeedParabolicRoot);
+ m_intermediates->falloffIntercept = std::pow(tangentInitialY, 2) - m_intermediates->falloffSlope * m_parameters.tangentSpeedParabolicRoot;
+ m_intermediates->tangentStartX = m_parameters.tangentSpeedParabolicRoot;
+ }
+}
+
+float ScrollingAccelerationCurve::evaluateQuartic(float x) const
+{
+ return std::pow(m_parameters.gainQuartic * x, 4) + std::pow(m_parameters.gainCubic * x, 3) + std::pow(m_parameters.gainParabolic * x, 2) + m_parameters.gainLinear * x;
+}
+
+float ScrollingAccelerationCurve::accelerationFactor(float value)
+{
+ constexpr float frameRate = 67.f; // Why is this 67 and not 60?
+ constexpr float cursorScale = 96.f / frameRate;
+
+ computeIntermediateValuesIfNeeded();
+
+ float multiplier;
+ float deviceScale = m_parameters.resolution / frameRate;
+ value /= deviceScale;
+
+ if (value <= m_intermediates->tangentStartX)
+ multiplier = evaluateQuartic(value);
+ else if (value <= m_intermediates->falloffStartX && m_intermediates->tangentStartX == m_parameters.tangentSpeedLinear)
+ multiplier = m_intermediates->tangentSlope * value + m_intermediates->tangentIntercept;
+ else
+ multiplier = std::sqrt(m_intermediates->falloffSlope * value + m_intermediates->falloffIntercept);
+
+ return multiplier * cursorScale;
+}
+
+void ScrollingAccelerationCurve::encode(IPC::Encoder& encoder) const
+{
+ encoder << m_parameters.gainLinear;
+ encoder << m_parameters.gainParabolic;
+ encoder << m_parameters.gainCubic;
+ encoder << m_parameters.gainQuartic;
+
+ encoder << m_parameters.tangentSpeedLinear;
+ encoder << m_parameters.tangentSpeedParabolicRoot;
+
+ encoder << m_parameters.resolution;
+}
+
+std::optional<ScrollingAccelerationCurve> ScrollingAccelerationCurve::decode(IPC::Decoder& decoder)
+{
+ float gainLinear;
+ if (!decoder.decode(gainLinear))
+ return std::nullopt;
+ float gainParabolic;
+ if (!decoder.decode(gainParabolic))
+ return std::nullopt;
+ float gainCubic;
+ if (!decoder.decode(gainCubic))
+ return std::nullopt;
+ float gainQuartic;
+ if (!decoder.decode(gainQuartic))
+ return std::nullopt;
+
+ float tangentSpeedLinear;
+ if (!decoder.decode(tangentSpeedLinear))
+ return std::nullopt;
+ float tangentSpeedParabolicRoot;
+ if (!decoder.decode(tangentSpeedParabolicRoot))
+ return std::nullopt;
+
+ float resolution;
+ if (!decoder.decode(resolution))
+ return std::nullopt;
+
+ return { { gainLinear, gainParabolic, gainCubic, gainQuartic, tangentSpeedLinear, tangentSpeedParabolicRoot, resolution } };
+}
+
+TextStream& operator<<(TextStream& ts, const ScrollingAccelerationCurve& curve)
+{
+ TextStream::GroupScope group(ts);
+
+ ts << "ScrollingAccelerationCurve";
+
+ ts.dumpProperty("gainLinear", curve.m_parameters.gainLinear);
+ ts.dumpProperty("gainParabolic", curve.m_parameters.gainParabolic);
+ ts.dumpProperty("gainCubic", curve.m_parameters.gainCubic);
+ ts.dumpProperty("gainQuartic", curve.m_parameters.gainQuartic);
+ ts.dumpProperty("tangentSpeedLinear", curve.m_parameters.tangentSpeedLinear);
+ ts.dumpProperty("tangentSpeedParabolicRoot", curve.m_parameters.tangentSpeedParabolicRoot);
+ ts.dumpProperty("resolution", curve.m_parameters.resolution);
+
+ return ts;
+}
+
+#if !PLATFORM(MAC)
+
+std::optional<ScrollingAccelerationCurve> ScrollingAccelerationCurve::fromNativeWheelEvent(const NativeWebWheelEvent&)
+{
+ return std::nullopt;
+}
+
+#endif
+
+} // namespace WebKit
+
+#endif // ENABLE(MOMENTUM_EVENT_DISPATCHER)
Added: branches/safari-612.4.2.1-branch/Source/WebKit/Shared/ScrollingAccelerationCurve.h (0 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/Shared/ScrollingAccelerationCurve.h (rev 0)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/Shared/ScrollingAccelerationCurve.h 2021-12-08 21:25:37 UTC (rev 286717)
@@ -0,0 +1,101 @@
+/*
+ * 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
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+
+#include <WebCore/FloatSize.h>
+#include <wtf/text/WTFString.h>
+
+namespace IPC {
+class Decoder;
+class Encoder;
+}
+
+namespace WebKit {
+
+class NativeWebWheelEvent;
+
+class ScrollingAccelerationCurve {
+public:
+ ScrollingAccelerationCurve(float gainLinear, float gainParabolic, float gainCubic, float gainQuartic, float tangentSpeedLinear, float tangentSpeedParabolicRoot, float resolution);
+
+ static std::optional<ScrollingAccelerationCurve> fromNativeWheelEvent(const NativeWebWheelEvent&);
+
+ static ScrollingAccelerationCurve interpolate(const ScrollingAccelerationCurve& from, const ScrollingAccelerationCurve& to, float amount);
+
+ float accelerationFactor(float);
+
+ void encode(IPC::Encoder&) const;
+ static std::optional<ScrollingAccelerationCurve> decode(IPC::Decoder&);
+
+ bool operator==(const ScrollingAccelerationCurve& other) const
+ {
+ return m_parameters.gainLinear == other.m_parameters.gainLinear
+ && m_parameters.gainParabolic == other.m_parameters.gainParabolic
+ && m_parameters.gainCubic == other.m_parameters.gainCubic
+ && m_parameters.gainQuartic == other.m_parameters.gainQuartic
+ && m_parameters.tangentSpeedLinear == other.m_parameters.tangentSpeedLinear
+ && m_parameters.tangentSpeedParabolicRoot == other.m_parameters.tangentSpeedParabolicRoot
+ && m_parameters.resolution == other.m_parameters.resolution;
+ }
+
+ bool operator!=(const ScrollingAccelerationCurve& other) const { return !(*this == other); }
+
+private:
+ friend TextStream& operator<<(TextStream&, const ScrollingAccelerationCurve&);
+
+ void computeIntermediateValuesIfNeeded();
+
+ float evaluateQuartic(float) const;
+
+ struct {
+ float gainLinear { 0 };
+ float gainParabolic { 0 };
+ float gainCubic { 0 };
+ float gainQuartic { 0 };
+ float tangentSpeedLinear { 0 };
+ float tangentSpeedParabolicRoot { 0 };
+ float resolution { 0 };
+ } m_parameters;
+
+ struct ComputedIntermediateValues {
+ float tangentStartX { 0 };
+ float tangentSlope { 0 };
+ float tangentIntercept { 0 };
+ float falloffStartX { 0 };
+ float falloffSlope { 0 };
+ float falloffIntercept { 0 };
+ };
+
+ std::optional<ComputedIntermediateValues> m_intermediates;
+};
+
+TextStream& operator<<(TextStream&, const ScrollingAccelerationCurve&);
+
+} // namespace WebKit
+
+#endif // ENABLE(MOMENTUM_EVENT_DISPATCHER)
Added: branches/safari-612.4.2.1-branch/Source/WebKit/Shared/mac/ScrollingAccelerationCurveMac.mm (0 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/Shared/mac/ScrollingAccelerationCurveMac.mm (rev 0)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/Shared/mac/ScrollingAccelerationCurveMac.mm 2021-12-08 21:25:37 UTC (rev 286717)
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+#import "config.h"
+#import "ScrollingAccelerationCurve.h"
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER) && PLATFORM(MAC)
+
+#import "Logging.h"
+#import <pal/spi/cg/CoreGraphicsSPI.h>
+#import <pal/spi/cocoa/IOKitSPI.h>
+
+namespace WebKit {
+
+static float fromFixedPoint(float value)
+{
+ return value / 65536.0f;
+}
+
+static float readFixedPointParameter(NSDictionary *parameters, const char *key)
+{
+ return fromFixedPoint([[parameters objectForKey:@(key)] floatValue]);
+}
+
+static ScrollingAccelerationCurve fromIOHIDCurve(NSDictionary *parameters, float resolution)
+{
+ auto gainLinear = readFixedPointParameter(parameters, kHIDAccelGainLinearKey);
+ auto gainParabolic = readFixedPointParameter(parameters, kHIDAccelGainParabolicKey);
+ auto gainCubic = readFixedPointParameter(parameters, kHIDAccelGainCubicKey);
+ auto gainQuartic = readFixedPointParameter(parameters, kHIDAccelGainQuarticKey);
+
+ auto tangentSpeedLinear = readFixedPointParameter(parameters, kHIDAccelTangentSpeedLinearKey);
+ auto tangentSpeedParabolicRoot = readFixedPointParameter(parameters, kHIDAccelTangentSpeedParabolicRootKey);
+
+ return { gainLinear, gainParabolic, gainCubic, gainQuartic, tangentSpeedLinear, tangentSpeedParabolicRoot, resolution };
+}
+
+static ScrollingAccelerationCurve fromIOHIDCurveArrayWithAcceleration(NSArray<NSDictionary *> *ioHIDCurves, float desiredAcceleration, float resolution)
+{
+ __block size_t currentIndex = 0;
+ __block Vector<std::pair<float, ScrollingAccelerationCurve>> curves;
+
+ [ioHIDCurves enumerateObjectsUsingBlock:^(NSDictionary *parameters, NSUInteger i, BOOL *) {
+ auto curveAcceleration = readFixedPointParameter(parameters, kHIDAccelIndexKey);
+ auto curve = fromIOHIDCurve(parameters, resolution);
+
+ if (desiredAcceleration > curveAcceleration)
+ currentIndex = i;
+
+ curves.append({ curveAcceleration, curve });
+ }];
+
+ // Interpolation if desiredAcceleration is in between two curves.
+ if (curves[currentIndex].first < desiredAcceleration && (currentIndex + 1) < curves.size()) {
+ const auto& lowCurve = curves[currentIndex];
+ const auto& highCurve = curves[currentIndex + 1];
+ float ratio = (desiredAcceleration - lowCurve.first) / (highCurve.first - lowCurve.first);
+ return ScrollingAccelerationCurve::interpolate(lowCurve.second, highCurve.second, ratio);
+ }
+
+ return curves[currentIndex].second;
+}
+
+static RetainPtr<IOHIDEventSystemClientRef> createHIDClient()
+{
+ auto client = adoptCF(IOHIDEventSystemClientCreateWithType(nil, kIOHIDEventSystemClientTypePassive, nil));
+ IOHIDEventSystemClientSetDispatchQueue(client.get(), dispatch_get_main_queue());
+ IOHIDEventSystemClientActivate(client.get());
+ return client;
+}
+
+static std::optional<ScrollingAccelerationCurve> fromIOHIDDevice(IOHIDEventSenderID senderID)
+{
+ static NeverDestroyed<RetainPtr<IOHIDEventSystemClientRef>> client;
+ if (!client.get())
+ client.get() = createHIDClient();
+
+ RetainPtr<IOHIDServiceClientRef> ioHIDService = adoptCF(IOHIDEventSystemClientCopyServiceForRegistryID(client.get().get(), senderID));
+ if (!ioHIDService) {
+ RELEASE_LOG(ScrollAnimations, "ScrollingAccelerationCurve::fromIOHIDDevice did not find matching HID service");
+ return std::nullopt;
+ }
+
+ auto curves = adoptCF(dynamic_cf_cast<CFArrayRef>(IOHIDServiceClientCopyProperty(ioHIDService.get(), CFSTR(kHIDScrollAccelParametricCurvesKey))));
+ if (!curves) {
+ RELEASE_LOG(ScrollAnimations, "ScrollingAccelerationCurve::fromIOHIDDevice failed to look up curves");
+ return std::nullopt;
+ }
+
+ // FIXME: There is some additional fallback to implement here, though this seems usually sufficient.
+ auto scrollAccelerationType = adoptCF(dynamic_cf_cast<CFStringRef>(IOHIDServiceClientCopyProperty(ioHIDService.get(), CFSTR("HIDScrollAccelerationType"))));
+ if (!scrollAccelerationType) {
+ RELEASE_LOG(ScrollAnimations, "ScrollingAccelerationCurve::fromIOHIDDevice failed to look up acceleration type");
+ return std::nullopt;
+ }
+
+ auto scrollAcceleration = adoptCF(dynamic_cf_cast<CFNumberRef>(IOHIDServiceClientCopyProperty(ioHIDService.get(), scrollAccelerationType.get())));
+ if (!scrollAcceleration) {
+ RELEASE_LOG(ScrollAnimations, "ScrollingAccelerationCurve::fromIOHIDDevice failed to look up acceleration value");
+ return std::nullopt;
+ }
+
+ auto resolution = adoptCF(dynamic_cf_cast<CFNumberRef>(IOHIDServiceClientCopyProperty(ioHIDService.get(), CFSTR(kIOHIDScrollResolutionKey))));
+ if (!resolution) {
+ RELEASE_LOG(ScrollAnimations, "ScrollingAccelerationCurve::fromIOHIDDevice failed to look up resolution");
+ return std::nullopt;
+ }
+
+ return fromIOHIDCurveArrayWithAcceleration((NSArray *)curves.get(), fromFixedPoint([(NSNumber *)scrollAcceleration.get() floatValue]), fromFixedPoint([(NSNumber *)resolution.get() floatValue]));
+}
+
+std::optional<ScrollingAccelerationCurve> ScrollingAccelerationCurve::fromNativeWheelEvent(const NativeWebWheelEvent& nativeWebWheelEvent)
+{
+ NSEvent *event = nativeWebWheelEvent.nativeEvent();
+
+ auto cgEvent = event.CGEvent;
+ if (!cgEvent) {
+ RELEASE_LOG(ScrollAnimations, "ScrollingAccelerationCurve::fromNativeWheelEvent did not find CG event");
+ return std::nullopt;
+ }
+
+ auto hidEvent = adoptCF(CGEventCopyIOHIDEvent(cgEvent));
+ if (!hidEvent) {
+ RELEASE_LOG(ScrollAnimations, "ScrollingAccelerationCurve::fromNativeWheelEvent did not find HID event");
+ return std::nullopt;
+ }
+
+ return fromIOHIDDevice(IOHIDEventGetSenderID(hidEvent.get()));
+}
+
+} // namespace WebKit
+
+#endif // ENABLE(MOMENTUM_EVENT_DISPATCHER) && PLATFORM(MAC)
Modified: branches/safari-612.4.2.1-branch/Source/WebKit/Sources.txt (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/Sources.txt 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/Sources.txt 2021-12-08 21:25:37 UTC (rev 286717)
@@ -197,6 +197,7 @@
Shared/PrintInfo.cpp
Shared/RTCNetwork.cpp
Shared/RTCPacketOptions.cpp
+Shared/ScrollingAccelerationCurve.cpp
Shared/ServiceWorkerInitializationData.cpp
Shared/SessionState.cpp
Shared/ShareableBitmap.cpp @no-unify
@@ -722,6 +723,7 @@
WebProcess/WebPage/EventDispatcher.cpp
WebProcess/WebPage/FindController.cpp
WebProcess/WebPage/IPCTestingAPI.cpp
+WebProcess/WebPage/MomentumEventDispatcher.cpp
WebProcess/WebPage/PageBanner.cpp
WebProcess/WebPage/VisitedLinkTableController.cpp
WebProcess/WebPage/WebBackForwardListProxy.cpp
Modified: branches/safari-612.4.2.1-branch/Source/WebKit/SourcesCocoa.txt (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/SourcesCocoa.txt 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/SourcesCocoa.txt 2021-12-08 21:25:37 UTC (rev 286717)
@@ -213,6 +213,7 @@
Shared/mac/ObjCObjectGraph.mm
Shared/mac/PasteboardTypes.mm
Shared/mac/PrintInfoMac.mm
+Shared/mac/ScrollingAccelerationCurveMac.mm
Shared/mac/SecItemRequestData.cpp
Shared/mac/SecItemResponseData.cpp
Shared/mac/SecItemShim.cpp
Modified: branches/safari-612.4.2.1-branch/Source/WebKit/UIProcess/WebPageProxy.cpp (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/UIProcess/WebPageProxy.cpp 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/UIProcess/WebPageProxy.cpp 2021-12-08 21:25:37 UTC (rev 286717)
@@ -2886,6 +2886,12 @@
closeOverlayedViews();
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+ // FIXME: We should not have to look this up repeatedly, but it can also change occasionally.
+ if (event.momentumPhase() == WebWheelEvent::PhaseBegan && preferences().momentumScrollingAnimatorEnabled())
+ m_scrollingAccelerationCurve = ScrollingAccelerationCurve::fromNativeWheelEvent(event);
+#endif
+
if (wheelEventCoalescer().shouldDispatchEvent(event)) {
auto event = wheelEventCoalescer().nextEventToDispatch();
sendWheelEvent(*event);
@@ -2931,8 +2937,16 @@
rubberBandableEdges.setLeft(!m_backForwardList->backItem());
rubberBandableEdges.setRight(!m_backForwardList->forwardItem());
}
- connection -> send(Messages::EventDispatcher::WheelEvent(m_webPageID, event, rubberBandableEdges), 0);
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+ if (event.momentumPhase() == WebWheelEvent::PhaseBegan && m_scrollingAccelerationCurve != m_lastSentScrollingAccelerationCurve) {
+ connection->send(Messages::EventDispatcher::SetScrollingAccelerationCurve(m_webPageID, *m_scrollingAccelerationCurve), 0, { }, Thread::QOS::UserInteractive);
+ m_lastSentScrollingAccelerationCurve = m_scrollingAccelerationCurve;
+ }
+#endif
+
+ connection->send(Messages::EventDispatcher::WheelEvent(m_webPageID, event, rubberBandableEdges), 0, { }, Thread::QOS::UserInteractive);
+
// Manually ping the web process to check for responsiveness since our wheel
// event will dispatch to a non-main thread, which always responds.
m_process->isResponsiveWithLazyStop();
@@ -3993,6 +4007,7 @@
if (!hasRunningProcess())
return;
+ send(Messages::EventDispatcher::PageScreenDidChange(m_webPageID, displayID));
send(Messages::WebPage::WindowScreenDidChange(displayID, nominalFramesPerSecond));
}
@@ -7905,6 +7920,10 @@
m_xrSystem = nullptr;
}
#endif
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+ m_lastSentScrollingAccelerationCurve = std::nullopt;
+#endif
}
void WebPageProxy::resetStateAfterProcessExited(ProcessTerminationReason terminationReason)
Modified: branches/safari-612.4.2.1-branch/Source/WebKit/UIProcess/WebPageProxy.h (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/UIProcess/WebPageProxy.h 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/UIProcess/WebPageProxy.h 2021-12-08 21:25:37 UTC (rev 286717)
@@ -51,6 +51,7 @@
#include "ProcessTerminationReason.h"
#include "ProcessThrottler.h"
#include "SandboxExtension.h"
+#include "ScrollingAccelerationCurve.h"
#include "ShareableBitmap.h"
#include "ShareableResource.h"
#include "SpeechRecognitionPermissionRequest.h"
@@ -3092,6 +3093,11 @@
#endif
bool m_needsSiteSpecificViewportQuirks { true };
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+ std::optional<ScrollingAccelerationCurve> m_scrollingAccelerationCurve;
+ std::optional<ScrollingAccelerationCurve> m_lastSentScrollingAccelerationCurve;
+#endif
};
#ifdef __OBJC__
Modified: branches/safari-612.4.2.1-branch/Source/WebKit/WebKit.xcodeproj/project.pbxproj (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/WebKit.xcodeproj/project.pbxproj 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/WebKit.xcodeproj/project.pbxproj 2021-12-08 21:25:37 UTC (rev 286717)
@@ -3274,6 +3274,9 @@
2D5C9D0419C81D8F00B3C5C1 /* WebPageOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebPageOverlay.h; sourceTree = "<group>"; };
2D6482492644B1AB00030335 /* RemoteLayerTreeLayers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteLayerTreeLayers.h; sourceTree = "<group>"; };
2D64824A2644B1AB00030335 /* RemoteLayerTreeLayers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RemoteLayerTreeLayers.mm; sourceTree = "<group>"; };
+ 2D65D194274B8E73009C4101 /* ScrollingAccelerationCurveMac.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ScrollingAccelerationCurveMac.mm; sourceTree = "<group>"; };
+ 2D65D195274B8E84009C4101 /* ScrollingAccelerationCurve.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScrollingAccelerationCurve.h; sourceTree = "<group>"; };
+ 2D65D196274B8E84009C4101 /* ScrollingAccelerationCurve.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScrollingAccelerationCurve.cpp; sourceTree = "<group>"; };
2D6AB53F192B1C4A003A9FD1 /* WKPDFPageNumberIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WKPDFPageNumberIndicator.h; path = ios/WKPDFPageNumberIndicator.h; sourceTree = "<group>"; };
2D6AB540192B1C4A003A9FD1 /* WKPDFPageNumberIndicator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = WKPDFPageNumberIndicator.mm; path = ios/WKPDFPageNumberIndicator.mm; sourceTree = "<group>"; };
2D6B371918A967AD0042AE80 /* _WKThumbnailView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKThumbnailView.h; sourceTree = "<group>"; };
@@ -3556,6 +3559,8 @@
2DE9B1382231F61C005287B7 /* _WKTextInputContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKTextInputContext.h; sourceTree = "<group>"; };
2DE9B13B2231F77C005287B7 /* _WKTextInputContextInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKTextInputContextInternal.h; sourceTree = "<group>"; };
2DEAC5CE1AC368BB00A195D8 /* _WKFindOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKFindOptions.h; sourceTree = "<group>"; };
+ 2DEE709D2755A46800FBF864 /* MomentumEventDispatcher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MomentumEventDispatcher.h; sourceTree = "<group>"; };
+ 2DEE709E2755A46800FBF864 /* MomentumEventDispatcher.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MomentumEventDispatcher.cpp; sourceTree = "<group>"; };
2DF9593418A42412009785A1 /* ViewGestureControllerIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ViewGestureControllerIOS.mm; path = ios/ViewGestureControllerIOS.mm; sourceTree = "<group>"; };
2DF9EEE31A781FB400B6CFBE /* APIFrameInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = APIFrameInfo.cpp; sourceTree = "<group>"; };
2DF9EEE41A781FB400B6CFBE /* APIFrameInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIFrameInfo.h; sourceTree = "<group>"; };
@@ -6895,6 +6900,8 @@
BC2D021612AC41CB00E732A3 /* SameDocumentNavigationType.h */,
1AAB4A8C1296F0A20023952F /* SandboxExtension.h */,
E1E552C316AE065E004ED653 /* SandboxInitializationParameters.h */,
+ 2D65D196274B8E84009C4101 /* ScrollingAccelerationCurve.cpp */,
+ 2D65D195274B8E84009C4101 /* ScrollingAccelerationCurve.h */,
5C80B3DD23690F100086E6DE /* ServiceWorkerInitializationData.cpp */,
5C80B3DB23690D8D0086E6DE /* ServiceWorkerInitializationData.h */,
1AFDE6571954A42B00C48FFA /* SessionState.cpp */,
@@ -10071,6 +10078,8 @@
1A90C1F21264FD71003E44D4 /* FindController.h */,
9B033E72252580F300501071 /* IPCTestingAPI.cpp */,
9B033E71252580F300501071 /* IPCTestingAPI.h */,
+ 2DEE709E2755A46800FBF864 /* MomentumEventDispatcher.cpp */,
+ 2DEE709D2755A46800FBF864 /* MomentumEventDispatcher.h */,
7C387433172F5615001BD88A /* PageBanner.cpp */,
7CF47FF917275C57008ACB91 /* PageBanner.h */,
2D819B99186275B3001F03D1 /* ViewGestureGeometryCollector.cpp */,
@@ -10643,6 +10652,7 @@
C574A58012E66681002DFE98 /* PasteboardTypes.mm */,
C1E123B920A11572002646F4 /* PDFContextMenu.h */,
E1CC1B8F12D7EADF00625838 /* PrintInfoMac.mm */,
+ 2D65D194274B8E73009C4101 /* ScrollingAccelerationCurveMac.mm */,
51D1304F1382EAC000351EDD /* SecItemRequestData.cpp */,
51D130501382EAC000351EDD /* SecItemRequestData.h */,
51D130511382EAC000351EDD /* SecItemResponseData.cpp */,
Modified: branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp 2021-12-08 21:25:37 UTC (rev 286717)
@@ -27,6 +27,7 @@
#include "EventDispatcher.h"
#include "EventDispatcherMessages.h"
+#include "MomentumEventDispatcher.h"
#include "WebEventConversion.h"
#include "WebPage.h"
#include "WebPageProxyMessages.h"
@@ -62,6 +63,9 @@
EventDispatcher::EventDispatcher()
: m_queue(WorkQueue::create("com.apple.WebKit.EventDispatcher", WorkQueue::Type::Serial, WorkQueue::QOS::UserInteractive))
, m_recentWheelEventDeltaFilter(WheelEventDeltaFilter::create())
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+ , m_momentumEventDispatcher(WTF::makeUnique<MomentumEventDispatcher>(*this))
+#endif
{
}
@@ -96,7 +100,7 @@
connection->addWorkQueueMessageReceiver(Messages::EventDispatcher::messageReceiverName(), m_queue.get(), this);
}
-void EventDispatcher::wheelEvent(PageIdentifier pageID, const WebWheelEvent& wheelEvent, RectEdges<bool> rubberBandableEdges)
+void EventDispatcher::internalWheelEvent(PageIdentifier pageID, const WebWheelEvent& wheelEvent, RectEdges<bool> rubberBandableEdges, WheelEventOrigin wheelEventOrigin)
{
#if PLATFORM(COCOA) || ENABLE(SCROLLING_THREAD)
PlatformWheelEvent platformWheelEvent = platform(wheelEvent);
@@ -128,7 +132,7 @@
auto scrollingTree = m_scrollingTrees.get(pageID);
if (!scrollingTree) {
- dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps);
+ dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps, wheelEventOrigin);
break;
}
@@ -143,10 +147,10 @@
scrollingTree->willProcessWheelEvent();
- ScrollingThread::dispatch([scrollingTree, wheelEvent, platformWheelEvent, processingSteps, pageID, protectedThis = makeRef(*this)] {
+ ScrollingThread::dispatch([scrollingTree, wheelEvent, platformWheelEvent, processingSteps, pageID, wheelEventOrigin, protectedThis = makeRef(*this)] {
if (processingSteps.contains(WheelEventProcessingSteps::MainThreadForScrolling)) {
scrollingTree->willSendEventToMainThread(platformWheelEvent);
- protectedThis->dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps);
+ protectedThis->dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps, wheelEventOrigin);
scrollingTree->waitForEventToBeProcessedByMainThread(platformWheelEvent);
return;
}
@@ -154,7 +158,7 @@
auto result = scrollingTree->handleWheelEvent(platformWheelEvent, processingSteps);
if (result.needsMainThreadProcessing()) {
- protectedThis->dispatchWheelEventViaMainThread(pageID, wheelEvent, result.steps);
+ protectedThis->dispatchWheelEventViaMainThread(pageID, wheelEvent, result.steps, wheelEventOrigin);
if (result.steps.contains(WheelEventProcessingSteps::MainThreadForScrolling))
return;
}
@@ -161,16 +165,35 @@
// If we scrolled on the scrolling thread (even if we send the event to the main thread for passive event handlers)
// respond to the UI process that the event was handled.
- protectedThis->sendDidReceiveEvent(pageID, wheelEvent.type(), result.wasHandled);
+ if (wheelEventOrigin == WheelEventOrigin::UIProcess)
+ protectedThis->sendDidReceiveEvent(pageID, wheelEvent.type(), result.wasHandled);
});
} while (false);
#else
UNUSED_PARAM(rubberBandableEdges);
- dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps);
+ dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps, wheelEventOrigin);
#endif
}
+void EventDispatcher::wheelEvent(PageIdentifier pageID, const WebWheelEvent& wheelEvent, RectEdges<bool> rubberBandableEdges)
+{
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+ if (m_momentumEventDispatcher->handleWheelEvent(pageID, wheelEvent, rubberBandableEdges)) {
+ sendDidReceiveEvent(pageID, wheelEvent.type(), true);
+ return;
+ }
+#endif
+ internalWheelEvent(pageID, wheelEvent, rubberBandableEdges, WheelEventOrigin::UIProcess);
+}
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+void EventDispatcher::setScrollingAccelerationCurve(PageIdentifier pageID, std::optional<ScrollingAccelerationCurve> curve)
+{
+ m_momentumEventDispatcher->setScrollingAccelerationCurve(pageID, curve);
+}
+#endif
+
#if ENABLE(MAC_GESTURE_EVENTS)
void EventDispatcher::gestureEvent(PageIdentifier pageID, const WebKit::WebGestureEvent& gestureEvent)
{
@@ -237,15 +260,15 @@
}
#endif
-void EventDispatcher::dispatchWheelEventViaMainThread(WebCore::PageIdentifier pageID, const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps)
+void EventDispatcher::dispatchWheelEventViaMainThread(WebCore::PageIdentifier pageID, const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps, WheelEventOrigin wheelEventOrigin)
{
ASSERT(!RunLoop::isMain());
- RunLoop::main().dispatch([protectedThis = makeRef(*this), pageID, wheelEvent, steps = processingSteps - WheelEventProcessingSteps::ScrollingThread]() mutable {
- protectedThis->dispatchWheelEvent(pageID, wheelEvent, steps);
+ RunLoop::main().dispatch([protectedThis = makeRef(*this), pageID, wheelEvent, wheelEventOrigin, steps = processingSteps - WheelEventProcessingSteps::ScrollingThread]() mutable {
+ protectedThis->dispatchWheelEvent(pageID, wheelEvent, steps, wheelEventOrigin);
});
}
-void EventDispatcher::dispatchWheelEvent(PageIdentifier pageID, const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps)
+void EventDispatcher::dispatchWheelEvent(PageIdentifier pageID, const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps, WheelEventOrigin wheelEventOrigin)
{
ASSERT(RunLoop::isMain());
@@ -253,7 +276,7 @@
if (!webPage)
return;
- webPage->wheelEvent(wheelEvent, processingSteps);
+ webPage->wheelEvent(wheelEvent, processingSteps, wheelEventOrigin);
}
#if ENABLE(MAC_GESTURE_EVENTS)
@@ -291,6 +314,11 @@
tracePoint(DisplayRefreshDispatchingToMainThread, displayID, sendToMainThread);
ASSERT(!RunLoop::isMain());
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+ m_momentumEventDispatcher->displayWasRefreshed(displayID, displayUpdate);
+#endif
+
notifyScrollingTreesDisplayWasRefreshed(displayID);
if (!sendToMainThread)
@@ -302,4 +330,14 @@
}
#endif
+void EventDispatcher::pageScreenDidChange(PageIdentifier pageID, PlatformDisplayID displayID)
+{
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+ m_momentumEventDispatcher->pageScreenDidChange(pageID, displayID);
+#else
+ UNUSED_PARAM(pageID);
+ UNUSED_PARAM(displayID);
+#endif
+}
+
} // namespace WebKit
Modified: branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/EventDispatcher.h (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/EventDispatcher.h 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/EventDispatcher.h 2021-12-08 21:25:37 UTC (rev 286717)
@@ -51,6 +51,8 @@
namespace WebKit {
+class MomentumEventDispatcher;
+class ScrollingAccelerationCurve;
class WebPage;
class WebWheelEvent;
@@ -77,6 +79,9 @@
void notifyScrollingTreesDisplayWasRefreshed(WebCore::PlatformDisplayID);
+ enum class WheelEventOrigin : bool { UIProcess, MomentumEventDispatcher };
+ void internalWheelEvent(WebCore::PageIdentifier, const WebWheelEvent&, WebCore::RectEdges<bool> rubberBandableEdges, WheelEventOrigin);
+
private:
EventDispatcher();
@@ -85,6 +90,9 @@
// Message handlers
void wheelEvent(WebCore::PageIdentifier, const WebWheelEvent&, WebCore::RectEdges<bool> rubberBandableEdges);
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+ void setScrollingAccelerationCurve(WebCore::PageIdentifier, std::optional<ScrollingAccelerationCurve>);
+#endif
#if ENABLE(IOS_TOUCH_EVENTS)
void touchEvent(WebCore::PageIdentifier, const WebTouchEvent&, CompletionHandler<void(bool)>&&);
void touchEventWithoutCallback(WebCore::PageIdentifier, const WebTouchEvent&);
@@ -94,8 +102,8 @@
#endif
// This is called on the main thread.
- void dispatchWheelEvent(WebCore::PageIdentifier, const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>);
- void dispatchWheelEventViaMainThread(WebCore::PageIdentifier, const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>);
+ void dispatchWheelEvent(WebCore::PageIdentifier, const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>, WheelEventOrigin);
+ void dispatchWheelEventViaMainThread(WebCore::PageIdentifier, const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>, WheelEventOrigin);
#if ENABLE(IOS_TOUCH_EVENTS)
void dispatchTouchEvents();
@@ -116,6 +124,8 @@
void displayDidRefreshOnScrollingThread(WebCore::PlatformDisplayID);
#endif
+ void pageScreenDidChange(WebCore::PageIdentifier, WebCore::PlatformDisplayID);
+
Ref<WorkQueue> m_queue;
#if ENABLE(SCROLLING_THREAD)
@@ -127,6 +137,10 @@
Lock m_touchEventsLock;
HashMap<WebCore::PageIdentifier, TouchEventQueue> m_touchEvents WTF_GUARDED_BY_LOCK(m_touchEventsLock);
#endif
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+ std::unique_ptr<MomentumEventDispatcher> m_momentumEventDispatcher;
+#endif
};
} // namespace WebKit
Modified: branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/EventDispatcher.messages.in (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/EventDispatcher.messages.in 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/EventDispatcher.messages.in 2021-12-08 21:25:37 UTC (rev 286717)
@@ -32,4 +32,9 @@
#if HAVE(CVDISPLAYLINK)
DisplayWasRefreshed(uint32_t displayID, struct WebCore::DisplayUpdate update, bool sendToMainThread)
#endif
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+ SetScrollingAccelerationCurve(WebCore::PageIdentifier pageID, std::optional<WebKit::ScrollingAccelerationCurve> curve)
+#endif
+ PageScreenDidChange(WebCore::PageIdentifier pageID, uint32_t displayID)
}
Added: branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.cpp (0 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.cpp (rev 0)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.cpp 2021-12-08 21:25:37 UTC (rev 286717)
@@ -0,0 +1,460 @@
+/*
+ * 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 "MomentumEventDispatcher.h"
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+
+#include "EventDispatcher.h"
+#include "Logging.h"
+#include "WebProcessProxyMessages.h"
+#include <WebCore/DisplayRefreshMonitor.h>
+
+namespace WebKit {
+
+static constexpr Seconds deltaHistoryMaximumAge = 500_ms;
+static constexpr Seconds deltaHistoryMaximumInterval = 150_ms;
+static constexpr Seconds idealCurveFrameRate = 1_s / 60;
+
+MomentumEventDispatcher::MomentumEventDispatcher(EventDispatcher& dispatcher)
+ : m_observerID(DisplayLinkObserverID::generate())
+ , m_dispatcher(dispatcher)
+{
+}
+
+MomentumEventDispatcher::~MomentumEventDispatcher()
+{
+ stopDisplayLink();
+}
+
+bool MomentumEventDispatcher::eventShouldStartSyntheticMomentumPhase(WebCore::PageIdentifier pageIdentifier, const WebWheelEvent& event) const
+{
+ if (event.momentumPhase() != WebWheelEvent::PhaseBegan)
+ return false;
+
+ if (!m_accelerationCurves.contains(pageIdentifier)) {
+ RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher not using synthetic momentum phase: no acceleration curve");
+ return false;
+ }
+
+ if (!event.rawPlatformDelta()) {
+ RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher not using synthetic momentum phase: no raw platform delta on start event");
+ return false;
+ }
+
+ return true;
+}
+
+bool MomentumEventDispatcher::handleWheelEvent(WebCore::PageIdentifier pageIdentifier, const WebWheelEvent& event, WebCore::RectEdges<bool> rubberBandableEdges)
+{
+ m_lastRubberBandableEdges = rubberBandableEdges;
+ m_lastIncomingEvent = event;
+
+ bool isMomentumEvent = event.momentumPhase() != WebWheelEvent::PhaseNone;
+
+ if (m_currentGesture.active) {
+ bool pageIdentifierChanged = pageIdentifier != m_currentGesture.pageIdentifier;
+
+ // Any incoming wheel event other than a changed event for the current gesture
+ // should interrupt the animation; in the usual case, it will be
+ // momentumPhase == PhaseEnded that interrupts.
+ bool eventShouldInterruptGesture = !isMomentumEvent || event.momentumPhase() != WebWheelEvent::PhaseChanged;
+
+ if (pageIdentifierChanged || eventShouldInterruptGesture)
+ didEndMomentumPhase();
+ }
+
+ if (event.phase() == WebWheelEvent::PhaseBegan || event.phase() == WebWheelEvent::PhaseChanged) {
+ didReceiveScrollEvent(event);
+
+ if (auto lastActivePhaseDelta = event.rawPlatformDelta())
+ m_lastActivePhaseDelta = *lastActivePhaseDelta;
+ }
+
+ if (eventShouldStartSyntheticMomentumPhase(pageIdentifier, event))
+ didStartMomentumPhase(pageIdentifier, event);
+
+ bool isMomentumEventDuringSyntheticGesture = isMomentumEvent && m_currentGesture.active;
+
+#if !RELEASE_LOG_DISABLED
+ if (isMomentumEventDuringSyntheticGesture)
+ m_currentGesture.accumulatedEventOffset += event.delta();
+#endif
+
+ // Consume any normal momentum events while we're inside a synthetic momentum gesture.
+ return isMomentumEventDuringSyntheticGesture;
+}
+
+static float appKitScrollMultiplierForEvent(const WebWheelEvent& event)
+{
+ auto delta = event.delta();
+ auto unacceleratedDelta = event.unacceleratedScrollingDelta();
+ float multiplier = 1;
+
+ // FIXME: Can the AppKit multiplier ever differ by axis?
+ if (delta.width() && unacceleratedDelta.width())
+ multiplier = std::max(multiplier, delta.width() / unacceleratedDelta.width());
+ if (delta.height() && unacceleratedDelta.height())
+ multiplier = std::max(multiplier, delta.height() / unacceleratedDelta.height());
+
+ return multiplier;
+}
+
+void MomentumEventDispatcher::dispatchSyntheticMomentumEvent(WebWheelEvent::Phase phase, WebCore::FloatSize delta)
+{
+ ASSERT(m_currentGesture.active);
+ ASSERT(m_currentGesture.initiatingEvent);
+
+ auto appKitScrollMultiplier = appKitScrollMultiplierForEvent(*m_currentGesture.initiatingEvent);
+ auto appKitAcceleratedDelta = delta * appKitScrollMultiplier;
+ auto wheelTicks = appKitAcceleratedDelta / WebCore::Scrollbar::pixelsPerLineStep();
+ auto time = WallTime::now();
+
+ // FIXME: Ideally we would stick legitimate rawPlatformDeltas on the event,
+ // but currently nothing will consume them, and we'd have to keep track of them separately.
+ WebWheelEvent syntheticEvent(
+ WebEvent::Wheel,
+ m_currentGesture.initiatingEvent->position(),
+ m_currentGesture.initiatingEvent->globalPosition(),
+ appKitAcceleratedDelta,
+ wheelTicks,
+ WebWheelEvent::ScrollByPixelWheelEvent,
+ m_currentGesture.initiatingEvent->directionInvertedFromDevice(),
+ WebWheelEvent::PhaseNone,
+ phase,
+ true,
+ m_currentGesture.initiatingEvent->scrollCount(),
+ delta,
+ m_lastIncomingEvent->modifiers(),
+ time,
+ time,
+ { });
+ m_dispatcher.internalWheelEvent(m_currentGesture.pageIdentifier, syntheticEvent, m_lastRubberBandableEdges, EventDispatcher::WheelEventOrigin::MomentumEventDispatcher);
+}
+
+void MomentumEventDispatcher::didStartMomentumPhase(WebCore::PageIdentifier pageIdentifier, const WebWheelEvent& event)
+{
+ m_currentGesture.active = true;
+ m_currentGesture.pageIdentifier = pageIdentifier;
+ m_currentGesture.initiatingEvent = event;
+ m_currentGesture.currentOffset = { };
+ m_currentGesture.startTime = WallTime::now();
+ m_currentGesture.accelerationCurve = [&] () -> std::optional<ScrollingAccelerationCurve> {
+ auto curveIterator = m_accelerationCurves.find(m_currentGesture.pageIdentifier);
+ if (curveIterator == m_accelerationCurves.end())
+ return { };
+ return curveIterator->value;
+ }();
+
+ startDisplayLink();
+
+ // FIXME: The system falls back from the table to just generating deltas
+ // directly when the frame interval is within 20fps of idealCurveFrameRate;
+ // we should perhaps do the same.
+ buildOffsetTableWithInitialDelta(*event.rawPlatformDelta());
+
+ dispatchSyntheticMomentumEvent(WebWheelEvent::PhaseBegan, { });
+}
+
+void MomentumEventDispatcher::didEndMomentumPhase()
+{
+ ASSERT(m_currentGesture.active);
+
+ dispatchSyntheticMomentumEvent(WebWheelEvent::PhaseEnded, { });
+
+ RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher saw momentum end phase with total offset %.1f %.1f, duration %f (event offset would have been %.1f %.1f)", m_currentGesture.currentOffset.width(), m_currentGesture.currentOffset.height(), (WallTime::now() - m_currentGesture.startTime).seconds(), m_currentGesture.accumulatedEventOffset.width(), m_currentGesture.accumulatedEventOffset.height());
+
+ stopDisplayLink();
+
+ m_currentGesture = { };
+}
+
+void MomentumEventDispatcher::setScrollingAccelerationCurve(WebCore::PageIdentifier pageIdentifier, std::optional<ScrollingAccelerationCurve> curve)
+{
+ m_accelerationCurves.set(pageIdentifier, curve);
+
+#if USE_MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING
+ WTF::TextStream stream(WTF::TextStream::LineMode::SingleLine);
+ stream << curve;
+ RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher set curve %s", stream.release().utf8().data());
+#endif
+}
+
+WebCore::PlatformDisplayID MomentumEventDispatcher::displayID() const
+{
+ ASSERT(m_currentGesture.pageIdentifier);
+ auto displayIDIterator = m_displayIDs.find(m_currentGesture.pageIdentifier);
+ if (displayIDIterator == m_displayIDs.end())
+ return { };
+ return displayIDIterator->value;
+}
+
+void MomentumEventDispatcher::startDisplayLink()
+{
+ auto displayID = this->displayID();
+ if (!displayID) {
+ RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher failed to start display link");
+ return;
+ }
+
+ // FIXME: Switch down to lower-than-full-speed frame rates for the tail end of the curve.
+ WebProcess::singleton().parentProcessConnection()->send(Messages::WebProcessProxy::StartDisplayLink(m_observerID, displayID, FullSpeedFramesPerSecond), 0);
+ RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher starting display link for display %d", displayID);
+}
+
+void MomentumEventDispatcher::stopDisplayLink()
+{
+ auto displayID = this->displayID();
+ if (!displayID) {
+ RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher failed to stop display link");
+ return;
+ }
+
+ WebProcess::singleton().parentProcessConnection()->send(Messages::WebProcessProxy::StopDisplayLink(m_observerID, displayID), 0);
+ RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher stopping display link for display %d", displayID);
+}
+
+void MomentumEventDispatcher::pageScreenDidChange(WebCore::PageIdentifier pageID, WebCore::PlatformDisplayID displayID)
+{
+ bool affectsCurrentGesture = (pageID == m_currentGesture.pageIdentifier);
+ if (affectsCurrentGesture)
+ stopDisplayLink();
+
+ m_displayIDs.set(pageID, displayID);
+
+ if (affectsCurrentGesture)
+ startDisplayLink();
+}
+
+void MomentumEventDispatcher::displayWasRefreshed(WebCore::PlatformDisplayID displayID, const WebCore::DisplayUpdate&)
+{
+ if (!m_currentGesture.active)
+ return;
+
+ if (displayID != this->displayID())
+ return;
+
+ auto animationTime = WallTime::now() - m_currentGesture.startTime;
+ auto desiredOffset = offsetAtTime(animationTime);
+
+#if !USE(MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING)
+ // Intentional delta rounding (but at the end!).
+ FloatSize delta = roundedIntSize(desiredOffset - m_currentGesture.currentOffset);
+#else
+ FloatSize delta = desiredOffset - m_currentGesture.currentOffset;
+#endif
+
+ dispatchSyntheticMomentumEvent(WebWheelEvent::PhaseChanged, -delta);
+
+ m_currentGesture.currentOffset += delta;
+}
+
+void MomentumEventDispatcher::didReceiveScrollEventWithInterval(FloatSize size, Seconds frameInterval)
+{
+ auto push = [](HistoricalDeltas& deltas, Delta newDelta) {
+ bool directionChanged = deltas.size() && (deltas.first().rawPlatformDelta > 0) != (newDelta.rawPlatformDelta > 0);
+ if (directionChanged || newDelta.frameInterval > deltaHistoryMaximumAge)
+ deltas.clear();
+
+ deltas.prepend(newDelta);
+ if (deltas.size() > deltaHistoryQueueSize)
+ deltas.removeLast();
+ };
+
+ push(m_deltaHistoryX, { size.width(), frameInterval });
+ push(m_deltaHistoryY, { size.height(), frameInterval });
+}
+
+void MomentumEventDispatcher::didReceiveScrollEvent(const WebWheelEvent& event)
+{
+ ASSERT(!m_currentGesture.active);
+
+ if (!event.rawPlatformDelta())
+ return;
+
+ auto delta = *event.rawPlatformDelta();
+ auto time = event.ioHIDEventTimestamp();
+
+ if (!m_lastScrollTimestamp) {
+ m_lastScrollTimestamp = time;
+ // FIXME: Check that this matches the system (they may go with 10ms for the first frame).
+ return;
+ }
+
+ auto frameInterval = time - *m_lastScrollTimestamp;
+ m_lastScrollTimestamp = time;
+
+ didReceiveScrollEventWithInterval(delta, frameInterval);
+}
+
+void MomentumEventDispatcher::buildOffsetTableWithInitialDelta(FloatSize initialUnacceleratedDelta)
+{
+#if USE(MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING)
+ m_currentGesture.carryOffset = { };
+#endif
+ m_currentGesture.offsetTable.clear();
+
+ FloatSize accumulatedOffset;
+ FloatSize unacceleratedDelta = initialUnacceleratedDelta;
+
+ do {
+ FloatSize acceleratedDelta;
+ std::tie(unacceleratedDelta, acceleratedDelta) = computeNextDelta(unacceleratedDelta);
+
+ accumulatedOffset += acceleratedDelta;
+ m_currentGesture.offsetTable.append(accumulatedOffset);
+ } while (std::abs(unacceleratedDelta.width()) > 0.5 || std::abs(unacceleratedDelta.height()) > 0.5);
+
+ RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher built table with %ld frames, initial delta %f %f, distance %f %f (initial delta from last changed event %f %f)", m_currentGesture.offsetTable.size(), initialUnacceleratedDelta.width(), initialUnacceleratedDelta.height(), accumulatedOffset.width(), accumulatedOffset.height(), m_lastActivePhaseDelta.width(), m_lastActivePhaseDelta.height());
+}
+
+static float interpolate(float a, float b, float t)
+{
+ return a + t * (b - a);
+}
+
+FloatSize MomentumEventDispatcher::offsetAtTime(Seconds time)
+{
+ if (!m_currentGesture.offsetTable.size())
+ return { };
+
+ float fractionalFrameNumber = time.seconds() / idealCurveFrameRate.seconds();
+ unsigned long lowerFrameNumber = std::min<unsigned long>(m_currentGesture.offsetTable.size() - 1, floor(fractionalFrameNumber));
+ unsigned long upperFrameNumber = std::min<unsigned long>(m_currentGesture.offsetTable.size() - 1, lowerFrameNumber + 1);
+ float amount = fractionalFrameNumber - lowerFrameNumber;
+
+ return {
+ -interpolate(m_currentGesture.offsetTable[lowerFrameNumber].width(), m_currentGesture.offsetTable[upperFrameNumber].width(), amount),
+ -interpolate(m_currentGesture.offsetTable[lowerFrameNumber].height(), m_currentGesture.offsetTable[upperFrameNumber].height(), amount)
+ };
+}
+
+static float momentumDecayRate(FloatSize delta, Seconds frameInterval)
+{
+ constexpr float defaultDecay = 0.975f;
+ constexpr float tailVelocity = 250.f;
+ constexpr float tailDecay = 0.91f;
+ auto alpha = defaultDecay;
+
+ if (frameInterval) {
+ float initialVelocity = delta.diagonalLength() / frameInterval.seconds();
+ float weakMomentum = std::max<float>(0.f, (tailVelocity - initialVelocity) / tailVelocity);
+ alpha = defaultDecay - (defaultDecay - tailDecay) * weakMomentum;
+ }
+
+ return std::pow(alpha, (frameInterval.seconds() / 0.008f));
+}
+
+static constexpr float fromFixedPoint(float value)
+{
+ return value / 65536.0f;
+}
+
+std::pair<FloatSize, FloatSize> MomentumEventDispatcher::computeNextDelta(FloatSize currentUnacceleratedDelta)
+{
+ FloatSize unacceleratedDelta = currentUnacceleratedDelta;
+
+ float decayRate = momentumDecayRate(unacceleratedDelta, idealCurveFrameRate);
+ unacceleratedDelta.scale(decayRate);
+
+ auto quantizedUnacceleratedDelta = unacceleratedDelta;
+
+#if USE(MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING)
+ // Round and carry.
+ int32_t quantizedX = std::round(quantizedUnacceleratedDelta.width());
+ int32_t quantizedY = std::round(quantizedUnacceleratedDelta.height());
+
+ if (std::abs(quantizedUnacceleratedDelta.width()) < 1 && std::abs(quantizedUnacceleratedDelta.height()) < 1) {
+ float deltaXIncludingCarry = quantizedUnacceleratedDelta.width() + m_currentGesture.carryOffset.width();
+ float deltaYIncludingCarry = quantizedUnacceleratedDelta.height() + m_currentGesture.carryOffset.height();
+
+ // Intentional truncation.
+ quantizedX = deltaXIncludingCarry;
+ quantizedY = deltaYIncludingCarry;
+ m_currentGesture.carryOffset = { deltaXIncludingCarry - quantizedX, deltaYIncludingCarry - quantizedY };
+ }
+
+ quantizedUnacceleratedDelta = { static_cast<float>(quantizedX), static_cast<float>(quantizedY) };
+#endif
+
+ // The delta queue operates on pre-acceleration deltas, so insert the new event *before* accelerating.
+ didReceiveScrollEventWithInterval(quantizedUnacceleratedDelta, idealCurveFrameRate);
+
+ auto accelerateAxis = [&] (HistoricalDeltas& deltas, float value) {
+ float totalDelta = 0;
+ Seconds totalTime;
+ unsigned count = 0;
+
+ for (const auto& delta : deltas) {
+ totalDelta += std::abs(delta.rawPlatformDelta);
+ count++;
+
+ if (delta.frameInterval > deltaHistoryMaximumInterval) {
+ totalTime += deltaHistoryMaximumInterval;
+ break;
+ }
+
+ totalTime += delta.frameInterval;
+
+ if (totalTime >= deltaHistoryMaximumAge)
+ break;
+ }
+
+ auto averageFrameInterval = std::clamp(totalTime / count, 1_ms, deltaHistoryMaximumInterval);
+ float averageFrameIntervalMS = averageFrameInterval.milliseconds();
+ float averageDelta = totalDelta / count;
+
+#if !RELEASE_LOG_DISABLED
+ if (!m_currentGesture.didLogInitialQueueState)
+ RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher initial historical deltas: average delta %f, average time %fms, event count %d", averageDelta, averageFrameIntervalMS, count);
+#endif
+
+ constexpr float velocityGainA = fromFixedPoint(2.f);
+ constexpr float velocityGainB = fromFixedPoint(955.f);
+ constexpr float velocityConstant = fromFixedPoint(98369.f);
+ constexpr float minimumVelocity = fromFixedPoint(1.f);
+
+ float velocity = (velocityGainA * averageFrameIntervalMS * averageFrameIntervalMS - velocityGainB * averageFrameIntervalMS + velocityConstant) * averageDelta;
+ velocity = std::max<float>(velocity, minimumVelocity);
+
+ // FIXME: This math is incomplete if we need to support acceleration tables as well.
+ float multiplier = m_currentGesture.accelerationCurve->accelerationFactor(velocity) / velocity;
+ return value * multiplier;
+ };
+
+ FloatSize acceleratedDelta(
+ accelerateAxis(m_deltaHistoryX, quantizedUnacceleratedDelta.width()),
+ accelerateAxis(m_deltaHistoryY, quantizedUnacceleratedDelta.height())
+ );
+
+ m_currentGesture.didLogInitialQueueState = true;
+
+ return { unacceleratedDelta, acceleratedDelta };
+}
+
+} // namespace WebKit
+
+#endif
Added: branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.h (0 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.h (rev 0)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.h 2021-12-08 21:25:37 UTC (rev 286717)
@@ -0,0 +1,133 @@
+/*
+ * 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
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+
+// FIXME: Remove this once we decide which version we want.
+#define USE_MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING 0
+#define USE_MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING 1
+
+#include "ScrollingAccelerationCurve.h"
+#include "WebWheelEvent.h"
+#include <WebCore/FloatSize.h>
+#include <WebCore/PageIdentifier.h>
+#include <WebCore/RectEdges.h>
+#include <memory>
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+struct DisplayUpdate;
+using PlatformDisplayID = uint32_t;
+}
+
+namespace WebKit {
+
+class EventDispatcher;
+
+class MomentumEventDispatcher {
+ WTF_MAKE_NONCOPYABLE(MomentumEventDispatcher);
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ MomentumEventDispatcher(EventDispatcher&);
+ ~MomentumEventDispatcher();
+
+ bool handleWheelEvent(WebCore::PageIdentifier, const WebWheelEvent&, WebCore::RectEdges<bool> rubberBandableEdges);
+
+ void setScrollingAccelerationCurve(WebCore::PageIdentifier, std::optional<ScrollingAccelerationCurve>);
+
+ void displayWasRefreshed(WebCore::PlatformDisplayID, const WebCore::DisplayUpdate&);
+
+ void pageScreenDidChange(WebCore::PageIdentifier, WebCore::PlatformDisplayID);
+
+private:
+ void didStartMomentumPhase(WebCore::PageIdentifier, const WebWheelEvent&);
+ void didEndMomentumPhase();
+
+ bool eventShouldStartSyntheticMomentumPhase(WebCore::PageIdentifier, const WebWheelEvent&) const;
+
+ void startDisplayLink();
+ void stopDisplayLink();
+
+ WebCore::PlatformDisplayID displayID() const;
+
+ void dispatchSyntheticMomentumEvent(WebWheelEvent::Phase, WebCore::FloatSize delta);
+
+ void buildOffsetTableWithInitialDelta(FloatSize);
+
+ FloatSize offsetAtTime(Seconds);
+ std::pair<FloatSize, FloatSize> computeNextDelta(FloatSize currentUnacceleratedDelta);
+
+ void didReceiveScrollEventWithInterval(FloatSize, Seconds);
+ void didReceiveScrollEvent(const WebWheelEvent&);
+
+ struct Delta {
+ float rawPlatformDelta;
+ Seconds frameInterval;
+ };
+ static constexpr unsigned deltaHistoryQueueSize = 9;
+ typedef Deque<Delta, deltaHistoryQueueSize> HistoricalDeltas;
+ HistoricalDeltas m_deltaHistoryX;
+ HistoricalDeltas m_deltaHistoryY;
+
+ std::optional<WallTime> m_lastScrollTimestamp;
+ std::optional<WebWheelEvent> m_lastIncomingEvent;
+ WebCore::RectEdges<bool> m_lastRubberBandableEdges;
+#if !RELEASE_LOG_DISABLED
+ FloatSize m_lastActivePhaseDelta;
+#endif
+
+ struct {
+ bool active { false };
+
+ WebCore::PageIdentifier pageIdentifier;
+ std::optional<ScrollingAccelerationCurve> accelerationCurve;
+ std::optional<WebWheelEvent> initiatingEvent;
+
+ FloatSize currentOffset;
+ WallTime startTime;
+
+ Vector<FloatSize> offsetTable;
+
+#if !RELEASE_LOG_DISABLED
+ FloatSize accumulatedEventOffset;
+ bool didLogInitialQueueState { false };
+#endif
+
+#if USE(MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING)
+ FloatSize carryOffset;
+#endif
+ } m_currentGesture;
+
+ DisplayLinkObserverID m_observerID;
+ HashMap<WebCore::PageIdentifier, WebCore::PlatformDisplayID> m_displayIDs;
+ HashMap<WebCore::PageIdentifier, std::optional<ScrollingAccelerationCurve>> m_accelerationCurves;
+ EventDispatcher& m_dispatcher;
+};
+
+} // namespace WebKit
+
+#endif
Modified: branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/WebPage.cpp (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/WebPage.cpp 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/WebPage.cpp 2021-12-08 21:25:37 UTC (rev 286717)
@@ -3057,7 +3057,7 @@
return page->userInputBridge().handleWheelEvent(platformWheelEvent, processingSteps);
}
-bool WebPage::wheelEvent(const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps)
+bool WebPage::wheelEvent(const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps, EventDispatcher::WheelEventOrigin wheelEventOrigin)
{
m_userActivity.impulse();
@@ -3065,7 +3065,7 @@
bool handled = handleWheelEvent(wheelEvent, m_page.get(), processingSteps);
- if (processingSteps.contains(WheelEventProcessingSteps::MainThreadForScrolling))
+ if (processingSteps.contains(WheelEventProcessingSteps::MainThreadForScrolling) && wheelEventOrigin == EventDispatcher::WheelEventOrigin::UIProcess)
send(Messages::WebPageProxy::DidReceiveEvent(static_cast<uint32_t>(wheelEvent.type()), handled));
return handled;
@@ -3079,7 +3079,7 @@
#else
bool isCancelable = true;
#endif
- bool handled = this->wheelEvent(wheelEvent, { isCancelable ? WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch : WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch });
+ bool handled = this->wheelEvent(wheelEvent, { isCancelable ? WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch : WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch }, EventDispatcher::WheelEventOrigin::UIProcess);
completionHandler(handled);
}
Modified: branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/WebPage.h (286716 => 286717)
--- branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/WebPage.h 2021-12-08 21:25:30 UTC (rev 286716)
+++ branches/safari-612.4.2.1-branch/Source/WebKit/WebProcess/WebPage/WebPage.h 2021-12-08 21:25:37 UTC (rev 286717)
@@ -38,6 +38,7 @@
#include "DownloadID.h"
#include "DrawingAreaInfo.h"
#include "EditingRange.h"
+#include "EventDispatcher.h"
#include "FocusedElementInformation.h"
#include "GeolocationIdentifier.h"
#include "InjectedBundlePageContextMenuClient.h"
@@ -1062,7 +1063,7 @@
void startWaitingForContextMenuToShow() { m_waitingForContextMenuToShow = true; }
#endif
- bool wheelEvent(const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>);
+ bool wheelEvent(const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>, EventDispatcher::WheelEventOrigin);
void wheelEventHandlersChanged(bool);
void recomputeShortCircuitHorizontalWheelEventsState();