blob: 7c3703123ba6d4c6e375aa6e547e05747ddea3ac [file] [log] [blame]
/*
* Copyright (C) 2011, 2014-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "FloatPoint.h"
#include "FloatSize.h"
#include "RectEdges.h"
#include "ScrollAnimation.h"
#include "ScrollSnapAnimatorState.h"
#include "ScrollSnapOffsetsInfo.h"
#include "ScrollTypes.h"
#include "WheelEventTestMonitor.h"
#include <wtf/Deque.h>
#include <wtf/Noncopyable.h>
#include <wtf/RunLoop.h>
namespace WebCore {
class KeyboardScrollingAnimator;
class LayoutSize;
class PlatformWheelEvent;
class ScrollSnapAnimatorState;
class ScrollingEffectsController;
class ScrollableArea;
class WheelEventTestMonitor;
struct ScrollExtents;
class ScrollingEffectsControllerTimer : public RunLoop::TimerBase {
public:
ScrollingEffectsControllerTimer(RunLoop& runLoop, Function<void()>&& callback)
: RunLoop::TimerBase(runLoop)
, m_callback(WTFMove(callback))
{
}
void fired() final
{
m_callback();
}
private:
Function<void()> m_callback;
};
class ScrollingEffectsControllerClient {
protected:
virtual ~ScrollingEffectsControllerClient() = default;
public:
// Only used for non-animation timers.
virtual std::unique_ptr<ScrollingEffectsControllerTimer> createTimer(Function<void()>&&) = 0;
virtual void startAnimationCallback(ScrollingEffectsController&) = 0;
virtual void stopAnimationCallback(ScrollingEffectsController&) = 0;
virtual void updateKeyboardScrollPosition(MonotonicTime) { }
virtual KeyboardScrollingAnimator *keyboardScrollingAnimator() const { return nullptr; }
virtual bool allowsHorizontalScrolling() const = 0;
virtual bool allowsVerticalScrolling() const = 0;
virtual void immediateScrollBy(const FloatSize&, ScrollClamping = ScrollClamping::Clamped) = 0;
// If the current scroll position is within the overhang area, this function will cause
// the page to scroll to the nearest boundary point.
virtual void adjustScrollPositionToBoundsIfNecessary() = 0;
#if HAVE(RUBBER_BANDING)
virtual bool allowsHorizontalStretching(const PlatformWheelEvent&) const = 0;
virtual bool allowsVerticalStretching(const PlatformWheelEvent&) const = 0;
virtual IntSize stretchAmount() const = 0;
// "Pinned" means scrolled at or beyond the edge.
virtual bool isPinnedOnSide(BoxSide) const = 0;
virtual RectEdges<bool> edgePinnedState() const = 0;
virtual bool shouldRubberBandOnSide(BoxSide) const = 0;
virtual void willStartRubberBandAnimation() { }
virtual void didStopRubberBandAnimation() { }
virtual void rubberBandingStateChanged(bool) { }
#endif
virtual void deferWheelEventTestCompletionForReason(WheelEventTestMonitor::ScrollableAreaIdentifier, WheelEventTestMonitor::DeferReason) const { /* Do nothing */ }
virtual void removeWheelEventTestCompletionDeferralForReason(WheelEventTestMonitor::ScrollableAreaIdentifier, WheelEventTestMonitor::DeferReason) const { /* Do nothing */ }
virtual FloatPoint scrollOffset() const = 0;
virtual void willStartAnimatedScroll() { }
virtual void didStopAnimatedScroll() { }
virtual void willStartScrollSnapAnimation() { }
virtual void didStopScrollSnapAnimation() { }
virtual float pageScaleFactor() const = 0;
virtual ScrollExtents scrollExtents() const = 0;
virtual bool scrollAnimationEnabled() const { return true; }
};
class ScrollingEffectsController : public ScrollAnimationClient {
WTF_MAKE_NONCOPYABLE(ScrollingEffectsController);
public:
explicit ScrollingEffectsController(ScrollingEffectsControllerClient&);
virtual ~ScrollingEffectsController();
bool usesScrollSnap() const;
void stopAllTimers();
void scrollPositionChanged();
bool startAnimatedScrollToDestination(FloatPoint startOffset, FloatPoint destinationOffset);
bool retargetAnimatedScroll(FloatPoint newDestinationOffset);
void stopAnimatedScroll();
bool startMomentumScrollWithInitialVelocity(const FloatPoint& initialOffset, const FloatSize& initialVelocity, const FloatSize& initialDelta, const WTF::Function<FloatPoint(const FloatPoint&)>& destinationModifier);
void beginKeyboardScrolling();
void stopKeyboardScrolling();
// Should be called periodically by the client. Started by startAnimationCallback(), stopped by stopAnimationCallback().
void animationCallback(MonotonicTime);
void updateGestureInProgressState(const PlatformWheelEvent&);
void contentsSizeChanged();
void setSnapOffsetsInfo(const LayoutScrollSnapOffsetsInfo&);
const LayoutScrollSnapOffsetsInfo* snapOffsetsInfo() const;
void setActiveScrollSnapIndexForAxis(ScrollEventAxis, std::optional<unsigned>);
void updateActiveScrollSnapIndexForClientOffset();
void resnapAfterLayout();
std::optional<unsigned> activeScrollSnapIndexForAxis(ScrollEventAxis) const;
float adjustedScrollDestination(ScrollEventAxis, FloatPoint destinationOffset, float velocity, std::optional<float> originalOffset) const;
bool activeScrollSnapIndexDidChange() const { return m_activeScrollSnapIndexDidChange; }
// FIXME: This is never called. We never set m_activeScrollSnapIndexDidChange back to false.
void setScrollSnapIndexDidChange(bool state) { m_activeScrollSnapIndexDidChange = state; }
// Returns true if handled.
bool handleWheelEvent(const PlatformWheelEvent&);
bool isScrollSnapInProgress() const;
bool isUserScrollInProgress() const;
#if PLATFORM(MAC)
static FloatSize wheelDeltaBiasingTowardsVertical(const PlatformWheelEvent&);
// Returns true if handled.
bool processWheelEventForScrollSnap(const PlatformWheelEvent&);
void stopRubberBanding();
bool isRubberBandInProgress() const;
RectEdges<bool> rubberBandingEdges() const { return m_rubberBandingEdges; }
#endif
private:
void updateRubberBandAnimatingState();
void updateKeyboardScrollingAnimatingState(MonotonicTime);
void setIsAnimatingRubberBand(bool);
void setIsAnimatingScrollSnap(bool);
void setIsAnimatingKeyboardScrolling(bool);
void startScrollSnapAnimation();
void stopScrollSnapAnimation();
#if PLATFORM(MAC)
bool shouldOverrideMomentumScrolling() const;
void statelessSnapTransitionTimerFired();
void scheduleStatelessScrollSnap();
void startDeferringWheelEventTestCompletionDueToScrollSnapping();
void stopDeferringWheelEventTestCompletionDueToScrollSnapping();
bool modifyScrollDeltaForStretching(const PlatformWheelEvent&, FloatSize&, bool isHorizontallyStretched, bool isVerticallyStretched);
bool applyScrollDeltaWithStretching(const PlatformWheelEvent&, FloatSize, bool isHorizontallyStretched, bool isVerticallyStretched);
void startRubberBandAnimationIfNecessary();
void startRubberBandAnimation(const FloatPoint& targetOffset, const FloatSize& initialVelocity, const FloatSize& initialOverscroll);
void stopRubberBandAnimation();
void willStartRubberBandAnimation();
void didStopRubberBandAnimation();
bool shouldRubberBandOnSide(BoxSide) const;
bool isRubberBandInProgressInternal() const;
void updateRubberBandingState();
void updateRubberBandingEdges(IntSize clientStretch);
#endif
void startOrStopAnimationCallbacks();
// ScrollAnimationClient
void scrollAnimationDidUpdate(ScrollAnimation&, const FloatPoint& /* currentOffset */) final;
void scrollAnimationWillStart(ScrollAnimation&) final;
void scrollAnimationDidEnd(ScrollAnimation&) final;
ScrollExtents scrollExtentsForAnimation(ScrollAnimation&) final;
void adjustDeltaForSnappingIfNeeded(float& deltaX, float& deltaY);
#if ENABLE(KINETIC_SCROLLING) && !PLATFORM(MAC)
// Returns true if handled.
bool processWheelEventForKineticScrolling(const PlatformWheelEvent&);
Deque<PlatformWheelEvent> m_scrollHistory;
#endif
ScrollingEffectsControllerClient& m_client;
std::unique_ptr<ScrollAnimation> m_currentAnimation;
std::unique_ptr<ScrollSnapAnimatorState> m_scrollSnapState;
bool m_activeScrollSnapIndexDidChange { false };
bool m_isRunningAnimatingCallback { false };
bool m_isAnimatingRubberBand { false };
bool m_isAnimatingScrollSnap { false };
bool m_isAnimatingKeyboardScrolling { false };
bool m_inScrollGesture { false };
#if PLATFORM(MAC)
WallTime m_lastMomentumScrollTimestamp;
FloatSize m_unappliedOverscrollDelta;
FloatSize m_stretchScrollForce;
FloatSize m_momentumVelocity;
bool m_momentumScrollInProgress { false };
bool m_ignoreMomentumScrolls { false };
bool m_isRubberBanding { false };
FloatSize m_dragEndedScrollingVelocity;
std::unique_ptr<ScrollingEffectsControllerTimer> m_statelessSnapTransitionTimer;
#if HAVE(RUBBER_BANDING)
RectEdges<bool> m_rubberBandingEdges;
#endif
#if ASSERT_ENABLED
bool m_timersWereStopped { false };
#endif
#endif
};
} // namespace WebCore