Use "= default" to denote default constructor or destructor
[WebKit-https.git] / Source / WebCore / platform / ScrollAnimator.cpp
1 /*
2  * Copyright (C) 2014-2015 Apple Inc.  All rights reserved.
3  * Copyright (c) 2010, Google Inc. All rights reserved.
4  * 
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  * 
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  * 
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "ScrollAnimator.h"
34
35 #include "FloatPoint.h"
36 #include "LayoutSize.h"
37 #include "PlatformWheelEvent.h"
38 #include "ScrollableArea.h"
39 #include <algorithm>
40
41 namespace WebCore {
42
43 #if !ENABLE(SMOOTH_SCROLLING) && !PLATFORM(IOS) && !PLATFORM(MAC)
44 std::unique_ptr<ScrollAnimator> ScrollAnimator::create(ScrollableArea& scrollableArea)
45 {
46     return std::make_unique<ScrollAnimator>(scrollableArea);
47 }
48 #endif
49
50 ScrollAnimator::ScrollAnimator(ScrollableArea& scrollableArea)
51     : m_scrollableArea(scrollableArea)
52 #if ENABLE(CSS_SCROLL_SNAP) || ENABLE(RUBBER_BANDING)
53     , m_scrollController(*this)
54 #endif
55 {
56 }
57
58 ScrollAnimator::~ScrollAnimator() = default;
59
60 bool ScrollAnimator::scroll(ScrollbarOrientation orientation, ScrollGranularity, float step, float multiplier)
61 {
62     FloatPoint currentPosition = this->currentPosition();
63     FloatSize delta;
64     if (orientation == HorizontalScrollbar)
65         delta.setWidth(step * multiplier);
66     else
67         delta.setHeight(step * multiplier);
68
69     FloatPoint newPosition = FloatPoint(currentPosition + delta).constrainedBetween(m_scrollableArea.minimumScrollPosition(), m_scrollableArea.maximumScrollPosition());
70     if (currentPosition == newPosition)
71         return false;
72
73     m_currentPosition = newPosition;
74     notifyPositionChanged(newPosition - currentPosition);
75     return true;
76 }
77
78 void ScrollAnimator::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
79 {
80     FloatPoint newPositon = ScrollableArea::scrollPositionFromOffset(offset, toFloatSize(m_scrollableArea.scrollOrigin()));
81     FloatSize delta = newPositon - currentPosition();
82     m_currentPosition = newPositon;
83     notifyPositionChanged(delta);
84     updateActiveScrollSnapIndexForOffset();
85 }
86
87 #if ENABLE(CSS_SCROLL_SNAP)
88 #if PLATFORM(MAC)
89 bool ScrollAnimator::processWheelEventForScrollSnap(const PlatformWheelEvent& wheelEvent)
90 {
91     return m_scrollController.processWheelEventForScrollSnap(wheelEvent);
92 }
93 #endif
94
95 bool ScrollAnimator::activeScrollSnapIndexDidChange() const
96 {
97     return m_scrollController.activeScrollSnapIndexDidChange();
98 }
99
100 unsigned ScrollAnimator::activeScrollSnapIndexForAxis(ScrollEventAxis axis) const
101 {
102     return m_scrollController.activeScrollSnapIndexForAxis(axis);
103 }
104 #endif
105
106 bool ScrollAnimator::handleWheelEvent(const PlatformWheelEvent& e)
107 {
108 #if ENABLE(CSS_SCROLL_SNAP) && PLATFORM(MAC)
109     if (!m_scrollController.processWheelEventForScrollSnap(e))
110         return false;
111 #endif
112 #if PLATFORM(COCOA)
113     // Events in the PlatformWheelEventPhaseMayBegin phase have no deltas, and therefore never passes through the scroll handling logic below.
114     // This causes us to return with an 'unhandled' return state, even though this event was successfully processed.
115     //
116     // We receive at least one PlatformWheelEventPhaseMayBegin when starting main-thread scrolling (see FrameView::wheelEvent), which can
117     // fool the scrolling thread into attempting to handle the scroll, unless we treat the event as handled here.
118     if (e.phase() == PlatformWheelEventPhaseMayBegin)
119         return true;
120 #endif
121
122     Scrollbar* horizontalScrollbar = m_scrollableArea.horizontalScrollbar();
123     Scrollbar* verticalScrollbar = m_scrollableArea.verticalScrollbar();
124
125     // Accept the event if we have a scrollbar in that direction and can still
126     // scroll any further.
127     float deltaX = horizontalScrollbar ? e.deltaX() : 0;
128     float deltaY = verticalScrollbar ? e.deltaY() : 0;
129
130     bool handled = false;
131
132     ScrollGranularity granularity = ScrollByPixel;
133     IntSize maxForwardScrollDelta = m_scrollableArea.maximumScrollPosition() - m_scrollableArea.scrollPosition();
134     IntSize maxBackwardScrollDelta = m_scrollableArea.scrollPosition() - m_scrollableArea.minimumScrollPosition();
135     if ((deltaX < 0 && maxForwardScrollDelta.width() > 0)
136         || (deltaX > 0 && maxBackwardScrollDelta.width() > 0)
137         || (deltaY < 0 && maxForwardScrollDelta.height() > 0)
138         || (deltaY > 0 && maxBackwardScrollDelta.height() > 0)) {
139         handled = true;
140
141         if (deltaY) {
142             if (e.granularity() == ScrollByPageWheelEvent) {
143                 bool negative = deltaY < 0;
144                 deltaY = Scrollbar::pageStepDelta(m_scrollableArea.visibleHeight());
145                 if (negative)
146                     deltaY = -deltaY;
147             }
148             scroll(VerticalScrollbar, granularity, verticalScrollbar->pixelStep(), -deltaY);
149         }
150
151         if (deltaX) {
152             if (e.granularity() == ScrollByPageWheelEvent) {
153                 bool negative = deltaX < 0;
154                 deltaX = Scrollbar::pageStepDelta(m_scrollableArea.visibleWidth());
155                 if (negative)
156                     deltaX = -deltaX;
157             }
158             scroll(HorizontalScrollbar, granularity, horizontalScrollbar->pixelStep(), -deltaX);
159         }
160     }
161     return handled;
162 }
163
164 #if ENABLE(TOUCH_EVENTS)
165 bool ScrollAnimator::handleTouchEvent(const PlatformTouchEvent&)
166 {
167     return false;
168 }
169 #endif
170
171 void ScrollAnimator::setCurrentPosition(const FloatPoint& position)
172 {
173     m_currentPosition = position;
174     updateActiveScrollSnapIndexForOffset();
175 }
176
177 void ScrollAnimator::updateActiveScrollSnapIndexForOffset()
178 {
179 #if ENABLE(CSS_SCROLL_SNAP)
180     // FIXME: Needs offset/position disambiguation.
181     m_scrollController.setActiveScrollSnapIndicesForOffset(m_currentPosition.x(), m_currentPosition.y());
182     if (m_scrollController.activeScrollSnapIndexDidChange()) {
183         m_scrollableArea.setCurrentHorizontalSnapPointIndex(m_scrollController.activeScrollSnapIndexForAxis(ScrollEventAxis::Horizontal));
184         m_scrollableArea.setCurrentVerticalSnapPointIndex(m_scrollController.activeScrollSnapIndexForAxis(ScrollEventAxis::Vertical));
185     }
186 #endif
187 }
188
189 void ScrollAnimator::notifyPositionChanged(const FloatSize& delta)
190 {
191     UNUSED_PARAM(delta);
192     // FIXME: need to not map back and forth all the time.
193     m_scrollableArea.setScrollOffsetFromAnimation(m_scrollableArea.scrollOffsetFromPosition(roundedIntPoint(currentPosition())));
194 }
195
196 #if ENABLE(CSS_SCROLL_SNAP)
197 void ScrollAnimator::updateScrollSnapState()
198 {
199     m_scrollController.updateScrollSnapState(m_scrollableArea);
200 }
201
202 FloatPoint ScrollAnimator::scrollOffset() const
203 {
204     return m_currentPosition;
205 }
206
207 void ScrollAnimator::immediateScrollOnAxis(ScrollEventAxis axis, float delta)
208 {
209     FloatSize deltaSize;
210     if (axis == ScrollEventAxis::Horizontal)
211         deltaSize.setWidth(delta);
212     else
213         deltaSize.setHeight(delta);
214
215     scrollToOffsetWithoutAnimation(currentPosition() + deltaSize);
216 }
217
218 LayoutSize ScrollAnimator::scrollExtent() const
219 {
220     return m_scrollableArea.contentsSize();
221 }
222
223 FloatSize ScrollAnimator::viewportSize() const
224 {
225     return m_scrollableArea.visibleSize();
226 }
227
228 #endif
229
230 #if (ENABLE(CSS_SCROLL_SNAP) || ENABLE(RUBBER_BANDING)) && PLATFORM(MAC)
231 void ScrollAnimator::deferTestsForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) const
232 {
233     if (!m_wheelEventTestTrigger)
234         return;
235
236     m_wheelEventTestTrigger->deferTestsForReason(identifier, reason);
237 }
238
239 void ScrollAnimator::removeTestDeferralForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) const
240 {
241     if (!m_wheelEventTestTrigger)
242         return;
243     
244     m_wheelEventTestTrigger->removeTestDeferralForReason(identifier, reason);
245 }
246 #endif
247
248 } // namespace WebCore