1/*
2 * Copyright (C) 2012 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "ScrollingTreeScrollingNode.h"
28
29#if ENABLE(ASYNC_SCROLLING)
30
31#include "Logging.h"
32#include "ScrollingStateScrollingNode.h"
33#include "ScrollingStateTree.h"
34#include "ScrollingTree.h"
35#include <wtf/text/TextStream.h>
36
37namespace WebCore {
38
39ScrollingTreeScrollingNode::ScrollingTreeScrollingNode(ScrollingTree& scrollingTree, ScrollingNodeType nodeType, ScrollingNodeID nodeID)
40 : ScrollingTreeNode(scrollingTree, nodeType, nodeID)
41{
42}
43
44ScrollingTreeScrollingNode::~ScrollingTreeScrollingNode() = default;
45
46void ScrollingTreeScrollingNode::commitStateBeforeChildren(const ScrollingStateNode& stateNode)
47{
48 const ScrollingStateScrollingNode& state = downcast<ScrollingStateScrollingNode>(stateNode);
49
50 if (state.hasChangedProperty(ScrollingStateScrollingNode::ScrollableAreaSize))
51 m_scrollableAreaSize = state.scrollableAreaSize();
52
53 if (state.hasChangedProperty(ScrollingStateScrollingNode::TotalContentsSize)) {
54 if (scrollingTree().isRubberBandInProgress())
55 m_totalContentsSizeForRubberBand = m_totalContentsSize;
56 else
57 m_totalContentsSizeForRubberBand = state.totalContentsSize();
58
59 m_totalContentsSize = state.totalContentsSize();
60 }
61
62 if (state.hasChangedProperty(ScrollingStateScrollingNode::ReachableContentsSize))
63 m_reachableContentsSize = state.reachableContentsSize();
64
65 if (state.hasChangedProperty(ScrollingStateScrollingNode::ScrollPosition))
66 m_lastCommittedScrollPosition = state.scrollPosition();
67
68 if (state.hasChangedProperty(ScrollingStateScrollingNode::ParentRelativeScrollableRect))
69 m_parentRelativeScrollableRect = state.parentRelativeScrollableRect();
70
71 if (state.hasChangedProperty(ScrollingStateScrollingNode::ScrollOrigin))
72 m_scrollOrigin = state.scrollOrigin();
73
74#if ENABLE(CSS_SCROLL_SNAP)
75 if (state.hasChangedProperty(ScrollingStateScrollingNode::HorizontalSnapOffsets))
76 m_snapOffsetsInfo.horizontalSnapOffsets = state.horizontalSnapOffsets();
77
78 if (state.hasChangedProperty(ScrollingStateScrollingNode::VerticalSnapOffsets))
79 m_snapOffsetsInfo.verticalSnapOffsets = state.verticalSnapOffsets();
80
81 if (state.hasChangedProperty(ScrollingStateScrollingNode::HorizontalSnapOffsetRanges))
82 m_snapOffsetsInfo.horizontalSnapOffsetRanges = state.horizontalSnapOffsetRanges();
83
84 if (state.hasChangedProperty(ScrollingStateScrollingNode::VerticalSnapOffsetRanges))
85 m_snapOffsetsInfo.verticalSnapOffsetRanges = state.verticalSnapOffsetRanges();
86
87 if (state.hasChangedProperty(ScrollingStateScrollingNode::CurrentHorizontalSnapOffsetIndex))
88 m_currentHorizontalSnapPointIndex = state.currentHorizontalSnapPointIndex();
89
90 if (state.hasChangedProperty(ScrollingStateScrollingNode::CurrentVerticalSnapOffsetIndex))
91 m_currentVerticalSnapPointIndex = state.currentVerticalSnapPointIndex();
92#endif
93
94 if (state.hasChangedProperty(ScrollingStateScrollingNode::ScrollableAreaParams))
95 m_scrollableAreaParameters = state.scrollableAreaParameters();
96
97 if (state.hasChangedProperty(ScrollingStateScrollingNode::ExpectsWheelEventTestTrigger))
98 m_expectsWheelEventTestTrigger = state.expectsWheelEventTestTrigger();
99
100#if PLATFORM(COCOA)
101 if (state.hasChangedProperty(ScrollingStateScrollingNode::ScrollContainerLayer))
102 m_scrollContainerLayer = state.scrollContainerLayer();
103
104 if (state.hasChangedProperty(ScrollingStateScrollingNode::ScrolledContentsLayer))
105 m_scrolledContentsLayer = state.scrolledContentsLayer();
106#endif
107}
108
109void ScrollingTreeScrollingNode::commitStateAfterChildren(const ScrollingStateNode& stateNode)
110{
111 const ScrollingStateScrollingNode& scrollingStateNode = downcast<ScrollingStateScrollingNode>(stateNode);
112 if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::RequestedScrollPosition))
113 scrollingTree().scrollingTreeNodeRequestsScroll(scrollingNodeID(), scrollingStateNode.requestedScrollPosition(), scrollingStateNode.requestedScrollPositionRepresentsProgrammaticScroll());
114}
115
116ScrollingEventResult ScrollingTreeScrollingNode::handleWheelEvent(const PlatformWheelEvent&)
117{
118 return ScrollingEventResult::DidNotHandleEvent;
119}
120
121FloatPoint ScrollingTreeScrollingNode::clampScrollPosition(const FloatPoint& scrollPosition) const
122{
123 return scrollPosition.constrainedBetween(minimumScrollPosition(), maximumScrollPosition());
124}
125
126FloatPoint ScrollingTreeScrollingNode::minimumScrollPosition() const
127{
128 auto minimumScrollOffset = FloatPoint { };
129 return ScrollableArea::scrollPositionFromOffset(minimumScrollOffset, toFloatSize(scrollOrigin()));
130}
131
132FloatPoint ScrollingTreeScrollingNode::maximumScrollPosition() const
133{
134 FloatPoint contentSizePoint(totalContentsSize());
135 auto maximumScrollOffset = FloatPoint(contentSizePoint - scrollableAreaSize()).expandedTo(FloatPoint());
136 return ScrollableArea::scrollPositionFromOffset(maximumScrollOffset, toFloatSize(scrollOrigin()));
137}
138
139bool ScrollingTreeScrollingNode::scrollLimitReached(const PlatformWheelEvent& wheelEvent) const
140{
141 FloatPoint oldScrollPosition = currentScrollPosition();
142 FloatPoint newScrollPosition = oldScrollPosition + FloatSize(wheelEvent.deltaX(), -wheelEvent.deltaY());
143 newScrollPosition = newScrollPosition.constrainedBetween(minimumScrollPosition(), maximumScrollPosition());
144 return newScrollPosition == oldScrollPosition;
145}
146
147FloatPoint ScrollingTreeScrollingNode::adjustedScrollPosition(const FloatPoint& scrollPosition, ScrollPositionClamp clamp) const
148{
149 if (clamp == ScrollPositionClamp::ToContentEdges)
150 return clampScrollPosition(scrollPosition);
151
152 return scrollPosition;
153}
154
155void ScrollingTreeScrollingNode::scrollBy(const FloatSize& delta, ScrollPositionClamp clamp)
156{
157 scrollTo(currentScrollPosition() + delta, ScrollType::User, clamp);
158}
159
160void ScrollingTreeScrollingNode::scrollTo(const FloatPoint& position, ScrollType scrollType, ScrollPositionClamp clamp)
161{
162 if (position == m_currentScrollPosition)
163 return;
164
165 scrollingTree().setIsHandlingProgrammaticScroll(scrollType == ScrollType::Programmatic);
166
167 m_currentScrollPosition = adjustedScrollPosition(position, clamp);
168
169 LOG_WITH_STREAM(Scrolling, stream << "ScrollingTreeScrollingNode " << scrollingNodeID() << " scrollTo " << position << " (delta from last committed position " << (m_lastCommittedScrollPosition - m_currentScrollPosition) << ")");
170
171 updateViewportForCurrentScrollPosition();
172 currentScrollPositionChanged();
173
174 scrollingTree().setIsHandlingProgrammaticScroll(false);
175}
176
177void ScrollingTreeScrollingNode::currentScrollPositionChanged()
178{
179 repositionScrollingLayers();
180 repositionRelatedLayers();
181
182 scrollingTree().notifyRelatedNodesAfterScrollPositionChange(*this);
183 scrollingTree().scrollingTreeNodeDidScroll(*this);
184}
185
186bool ScrollingTreeScrollingNode::scrollPositionAndLayoutViewportMatch(const FloatPoint& position, Optional<FloatRect>)
187{
188 return position == m_currentScrollPosition;
189}
190
191void ScrollingTreeScrollingNode::applyLayerPositions()
192{
193 repositionScrollingLayers();
194 repositionRelatedLayers();
195}
196
197void ScrollingTreeScrollingNode::wasScrolledByDelegatedScrolling(const FloatPoint& position, Optional<FloatRect> overrideLayoutViewport)
198{
199 bool scrollPositionChanged = !scrollPositionAndLayoutViewportMatch(position, overrideLayoutViewport);
200 if (!scrollPositionChanged)
201 return;
202
203 m_currentScrollPosition = adjustedScrollPosition(position, ScrollPositionClamp::None);
204 updateViewportForCurrentScrollPosition(overrideLayoutViewport);
205
206 repositionRelatedLayers();
207
208 scrollingTree().notifyRelatedNodesAfterScrollPositionChange(*this);
209 scrollingTree().scrollingTreeNodeDidScroll(*this);
210 scrollingTree().didScrollByDelegatedScrolling();
211}
212
213LayoutPoint ScrollingTreeScrollingNode::parentToLocalPoint(LayoutPoint point) const
214{
215 return point - toLayoutSize(parentRelativeScrollableRect().location());
216}
217
218LayoutPoint ScrollingTreeScrollingNode::localToContentsPoint(LayoutPoint point) const
219{
220 return point + LayoutPoint(currentScrollPosition());
221}
222
223ScrollingTreeScrollingNode* ScrollingTreeScrollingNode::scrollingNodeForPoint(LayoutPoint parentPoint) const
224{
225 if (auto* node = ScrollingTreeNode::scrollingNodeForPoint(parentPoint))
226 return node;
227
228 if (parentRelativeScrollableRect().contains(parentPoint))
229 return const_cast<ScrollingTreeScrollingNode*>(this);
230
231 return nullptr;
232}
233
234void ScrollingTreeScrollingNode::dumpProperties(TextStream& ts, ScrollingStateTreeAsTextBehavior behavior) const
235{
236 ScrollingTreeNode::dumpProperties(ts, behavior);
237 ts.dumpProperty("scrollable area size", m_scrollableAreaSize);
238 ts.dumpProperty("total content size", m_totalContentsSize);
239 if (m_totalContentsSizeForRubberBand != m_totalContentsSize)
240 ts.dumpProperty("total content size for rubber band", m_totalContentsSizeForRubberBand);
241 if (m_reachableContentsSize != m_totalContentsSize)
242 ts.dumpProperty("reachable content size", m_reachableContentsSize);
243 ts.dumpProperty("last committed scroll position", m_lastCommittedScrollPosition);
244
245 if (!m_parentRelativeScrollableRect.isEmpty())
246 ts.dumpProperty("parent relative scrollable rect", m_parentRelativeScrollableRect);
247
248 if (m_scrollOrigin != IntPoint())
249 ts.dumpProperty("scroll origin", m_scrollOrigin);
250
251#if ENABLE(CSS_SCROLL_SNAP)
252 if (m_snapOffsetsInfo.horizontalSnapOffsets.size())
253 ts.dumpProperty("horizontal snap offsets", m_snapOffsetsInfo.horizontalSnapOffsets);
254
255 if (m_snapOffsetsInfo.verticalSnapOffsets.size())
256 ts.dumpProperty("vertical snap offsets", m_snapOffsetsInfo.verticalSnapOffsets);
257
258 if (m_currentHorizontalSnapPointIndex)
259 ts.dumpProperty("current horizontal snap point index", m_currentHorizontalSnapPointIndex);
260
261 if (m_currentVerticalSnapPointIndex)
262 ts.dumpProperty("current vertical snap point index", m_currentVerticalSnapPointIndex);
263
264#endif
265
266 ts.dumpProperty("scrollable area parameters", m_scrollableAreaParameters);
267}
268
269} // namespace WebCore
270
271#endif // ENABLE(ASYNC_SCROLLING)
272