Add CSS -webkit-appearance property for Apple Pay buttons
[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()
59 {
60 }
61
62 bool ScrollAnimator::scroll(ScrollbarOrientation orientation, ScrollGranularity, float step, float multiplier)
63 {
64     FloatPoint currentPosition = this->currentPosition();
65     FloatSize delta;
66     if (orientation == HorizontalScrollbar)
67         delta.setWidth(step * multiplier);
68     else
69         delta.setHeight(step * multiplier);
70
71     FloatPoint newPosition = FloatPoint(currentPosition + delta).constrainedBetween(m_scrollableArea.minimumScrollPosition(), m_scrollableArea.maximumScrollPosition());
72     if (currentPosition == newPosition)
73         return false;
74
75     m_currentPosition = newPosition;
76     notifyPositionChanged(newPosition - currentPosition);
77     return true;
78 }
79
80 void ScrollAnimator::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
81 {
82     FloatPoint newPositon = ScrollableArea::scrollPositionFromOffset(offset, toFloatSize(m_scrollableArea.scrollOrigin()));
83     FloatSize delta = newPositon - currentPosition();
84     m_currentPosition = newPositon;
85     notifyPositionChanged(delta);
86     updateActiveScrollSnapIndexForOffset();
87 }
88
89 #if ENABLE(CSS_SCROLL_SNAP)
90 #if PLATFORM(MAC)
91 bool ScrollAnimator::processWheelEventForScrollSnap(const PlatformWheelEvent& wheelEvent)
92 {
93     return m_scrollController.processWheelEventForScrollSnap(wheelEvent);
94 }
95 #endif
96
97 bool ScrollAnimator::activeScrollSnapIndexDidChange() const
98 {
99     return m_scrollController.activeScrollSnapIndexDidChange();
100 }
101
102 unsigned ScrollAnimator::activeScrollSnapIndexForAxis(ScrollEventAxis axis) const
103 {
104     return m_scrollController.activeScrollSnapIndexForAxis(axis);
105 }
106 #endif
107
108 bool ScrollAnimator::handleWheelEvent(const PlatformWheelEvent& e)
109 {
110 #if ENABLE(CSS_SCROLL_SNAP) && PLATFORM(MAC)
111     if (!m_scrollController.processWheelEventForScrollSnap(e))
112         return false;
113 #endif
114 #if PLATFORM(COCOA)
115     // Events in the PlatformWheelEventPhaseMayBegin phase have no deltas, and therefore never passes through the scroll handling logic below.
116     // This causes us to return with an 'unhandled' return state, even though this event was successfully processed.
117     //
118     // We receive at least one PlatformWheelEventPhaseMayBegin when starting main-thread scrolling (see FrameView::wheelEvent), which can
119     // fool the scrolling thread into attempting to handle the scroll, unless we treat the event as handled here.
120     if (e.phase() == PlatformWheelEventPhaseMayBegin)
121         return true;
122 #endif
123
124     Scrollbar* horizontalScrollbar = m_scrollableArea.horizontalScrollbar();
125     Scrollbar* verticalScrollbar = m_scrollableArea.verticalScrollbar();
126
127     // Accept the event if we have a scrollbar in that direction and can still
128     // scroll any further.
129     float deltaX = horizontalScrollbar ? e.deltaX() : 0;
130     float deltaY = verticalScrollbar ? e.deltaY() : 0;
131
132     bool handled = false;
133
134     ScrollGranularity granularity = ScrollByPixel;
135     IntSize maxForwardScrollDelta = m_scrollableArea.maximumScrollPosition() - m_scrollableArea.scrollPosition();
136     IntSize maxBackwardScrollDelta = m_scrollableArea.scrollPosition() - m_scrollableArea.minimumScrollPosition();
137     if ((deltaX < 0 && maxForwardScrollDelta.width() > 0)
138         || (deltaX > 0 && maxBackwardScrollDelta.width() > 0)
139         || (deltaY < 0 && maxForwardScrollDelta.height() > 0)
140         || (deltaY > 0 && maxBackwardScrollDelta.height() > 0)) {
141         handled = true;
142
143         if (deltaY) {
144             if (e.granularity() == ScrollByPageWheelEvent) {
145                 bool negative = deltaY < 0;
146                 deltaY = Scrollbar::pageStepDelta(m_scrollableArea.visibleHeight());
147                 if (negative)
148                     deltaY = -deltaY;
149             }
150             scroll(VerticalScrollbar, granularity, verticalScrollbar->pixelStep(), -deltaY);
151         }
152
153         if (deltaX) {
154             if (e.granularity() == ScrollByPageWheelEvent) {
155                 bool negative = deltaX < 0;
156                 deltaX = Scrollbar::pageStepDelta(m_scrollableArea.visibleWidth());
157                 if (negative)
158                     deltaX = -deltaX;
159             }
160             scroll(HorizontalScrollbar, granularity, horizontalScrollbar->pixelStep(), -deltaX);
161         }
162     }
163     return handled;
164 }
165
166 #if ENABLE(TOUCH_EVENTS)
167 bool ScrollAnimator::handleTouchEvent(const PlatformTouchEvent&)
168 {
169     return false;
170 }
171 #endif
172
173 void ScrollAnimator::setCurrentPosition(const FloatPoint& position)
174 {
175     m_currentPosition = position;
176     updateActiveScrollSnapIndexForOffset();
177 }
178
179 void ScrollAnimator::updateActiveScrollSnapIndexForOffset()
180 {
181 #if ENABLE(CSS_SCROLL_SNAP)
182     // FIXME: Needs offset/position disambiguation.
183     m_scrollController.setActiveScrollSnapIndicesForOffset(m_currentPosition.x(), m_currentPosition.y());
184     if (m_scrollController.activeScrollSnapIndexDidChange()) {
185         m_scrollableArea.setCurrentHorizontalSnapPointIndex(m_scrollController.activeScrollSnapIndexForAxis(ScrollEventAxis::Horizontal));
186         m_scrollableArea.setCurrentVerticalSnapPointIndex(m_scrollController.activeScrollSnapIndexForAxis(ScrollEventAxis::Vertical));
187     }
188 #endif
189 }
190
191 void ScrollAnimator::notifyPositionChanged(const FloatSize& delta)
192 {
193     UNUSED_PARAM(delta);
194     // FIXME: need to not map back and forth all the time.
195     m_scrollableArea.setScrollOffsetFromAnimation(m_scrollableArea.scrollOffsetFromPosition(roundedIntPoint(currentPosition())));
196 }
197
198 #if ENABLE(CSS_SCROLL_SNAP)
199 void ScrollAnimator::updateScrollSnapState()
200 {
201     m_scrollController.updateScrollSnapState(m_scrollableArea);
202 }
203
204 LayoutUnit ScrollAnimator::scrollOffsetOnAxis(ScrollEventAxis axis) const
205 {
206     return axis == ScrollEventAxis::Horizontal ? m_currentPosition.x() : m_currentPosition.y();
207 }
208
209 void ScrollAnimator::immediateScrollOnAxis(ScrollEventAxis axis, float delta)
210 {
211     FloatSize deltaSize;
212     if (axis == ScrollEventAxis::Horizontal)
213         deltaSize.setWidth(delta);
214     else
215         deltaSize.setHeight(delta);
216
217     scrollToOffsetWithoutAnimation(currentPosition() + deltaSize);
218 }
219
220 LayoutSize ScrollAnimator::scrollExtent() const
221 {
222     return m_scrollableArea.contentsSize();
223 }
224 #endif
225
226 #if (ENABLE(CSS_SCROLL_SNAP) || ENABLE(RUBBER_BANDING)) && PLATFORM(MAC)
227 void ScrollAnimator::deferTestsForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) const
228 {
229     if (!m_wheelEventTestTrigger)
230         return;
231
232     m_wheelEventTestTrigger->deferTestsForReason(identifier, reason);
233 }
234
235 void ScrollAnimator::removeTestDeferralForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) const
236 {
237     if (!m_wheelEventTestTrigger)
238         return;
239     
240     m_wheelEventTestTrigger->removeTestDeferralForReason(identifier, reason);
241 }
242 #endif
243
244 } // namespace WebCore