Split scrolling tree ScrollingNodes into FrameScrollingNodes and OverflowScrollingNodes
[WebKit-https.git] / Source / WebCore / page / scrolling / ios / ScrollingTreeFrameScrollingNodeIOS.mm
1 /*
2  * Copyright (C) 2014 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 #import "config.h"
27 #import "ScrollingTreeFrameScrollingNodeIOS.h"
28
29 #if ENABLE(ASYNC_SCROLLING)
30
31 #import "FrameView.h"
32 #import "ScrollingCoordinator.h"
33 #import "ScrollingTree.h"
34 #import "ScrollingStateTree.h"
35 #import "Settings.h"
36 #import "TileController.h"
37 #import "WebLayer.h"
38
39 #import <QuartzCore/QuartzCore.h>
40
41 namespace WebCore {
42
43 PassOwnPtr<ScrollingTreeFrameScrollingNodeIOS> ScrollingTreeFrameScrollingNodeIOS::create(ScrollingTree& scrollingTree, ScrollingNodeID nodeID)
44 {
45     return adoptPtr(new ScrollingTreeFrameScrollingNodeIOS(scrollingTree, nodeID));
46 }
47
48 ScrollingTreeFrameScrollingNodeIOS::ScrollingTreeFrameScrollingNodeIOS(ScrollingTree& scrollingTree, ScrollingNodeID nodeID)
49     : ScrollingTreeFrameScrollingNode(scrollingTree, nodeID)
50 {
51 }
52
53 ScrollingTreeFrameScrollingNodeIOS::~ScrollingTreeFrameScrollingNodeIOS()
54 {
55 }
56
57 void ScrollingTreeFrameScrollingNodeIOS::updateBeforeChildren(const ScrollingStateNode& stateNode)
58 {
59     ScrollingTreeFrameScrollingNode::updateBeforeChildren(stateNode);
60     
61     const auto& scrollingStateNode = toScrollingStateFrameScrollingNode(stateNode);
62
63     if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::ScrollLayer))
64         m_scrollLayer = scrollingStateNode.layer();
65
66     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::CounterScrollingLayer))
67         m_counterScrollingLayer = scrollingStateNode.counterScrollingLayer();
68
69     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::HeaderLayer))
70         m_headerLayer = scrollingStateNode.headerLayer();
71
72     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::FooterLayer))
73         m_footerLayer = scrollingStateNode.footerLayer();
74
75     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::ReasonsForSynchronousScrolling)) {
76         if (shouldUpdateScrollLayerPositionSynchronously()) {
77             // We're transitioning to the slow "update scroll layer position on the main thread" mode.
78             // Initialize the probable main thread scroll position with the current scroll layer position.
79             if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::RequestedScrollPosition))
80                 m_probableMainThreadScrollPosition = scrollingStateNode.requestedScrollPosition();
81             else {
82                 CGPoint scrollLayerPosition = m_scrollLayer.get().position;
83                 m_probableMainThreadScrollPosition = IntPoint(-scrollLayerPosition.x, -scrollLayerPosition.y);
84             }
85         }
86     }
87 }
88
89 void ScrollingTreeFrameScrollingNodeIOS::updateAfterChildren(const ScrollingStateNode& stateNode)
90 {
91     ScrollingTreeFrameScrollingNode::updateAfterChildren(stateNode);
92
93     const auto& scrollingStateNode = toScrollingStateFrameScrollingNode(stateNode);
94
95     // Update the scroll position after child nodes have been updated, because they need to have updated their constraints before any scrolling happens.
96     if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::RequestedScrollPosition))
97         setScrollPosition(scrollingStateNode.requestedScrollPosition());
98 }
99
100 FloatPoint ScrollingTreeFrameScrollingNodeIOS::scrollPosition() const
101 {
102     if (shouldUpdateScrollLayerPositionSynchronously())
103         return m_probableMainThreadScrollPosition;
104
105     CGPoint scrollLayerPosition = m_scrollLayer.get().position;
106     return IntPoint(-scrollLayerPosition.x + scrollOrigin().x(), -scrollLayerPosition.y + scrollOrigin().y());
107 }
108
109 void ScrollingTreeFrameScrollingNodeIOS::setScrollPosition(const FloatPoint& scrollPosition)
110 {
111     FloatPoint newScrollPosition = scrollPosition;
112     newScrollPosition = newScrollPosition.shrunkTo(maximumScrollPosition());
113     newScrollPosition = newScrollPosition.expandedTo(minimumScrollPosition());
114
115     setScrollPositionWithoutContentEdgeConstraints(newScrollPosition);
116 }
117
118 void ScrollingTreeFrameScrollingNodeIOS::setScrollPositionWithoutContentEdgeConstraints(const FloatPoint& scrollPosition)
119 {
120     if (shouldUpdateScrollLayerPositionSynchronously()) {
121         m_probableMainThreadScrollPosition = scrollPosition;
122         scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition, SetScrollingLayerPosition);
123         return;
124     }
125
126     setScrollLayerPosition(scrollPosition);
127     scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition);
128 }
129
130 void ScrollingTreeFrameScrollingNodeIOS::setScrollLayerPosition(const FloatPoint& scrollPosition)
131 {
132     ASSERT(!shouldUpdateScrollLayerPositionSynchronously());
133     [m_scrollLayer setPosition:CGPointMake(-scrollPosition.x() + scrollOrigin().x(), -scrollPosition.y() + scrollOrigin().y())];
134
135     updateChildNodesAfterScroll(scrollPosition);
136 }
137
138 void ScrollingTreeFrameScrollingNodeIOS::updateLayersAfterViewportChange(const FloatRect& viewportRect, double /*scale*/)
139 {
140     [m_counterScrollingLayer setPosition:viewportRect.location()];
141
142     if (!m_children)
143         return;
144
145     size_t size = m_children->size();
146     for (size_t i = 0; i < size; ++i)
147         m_children->at(i)->parentScrollPositionDidChange(viewportRect, FloatSize());
148 }
149
150 void ScrollingTreeFrameScrollingNodeIOS::updateLayersAfterDelegatedScroll(const FloatPoint& scrollPosition)
151 {
152     updateChildNodesAfterScroll(scrollPosition);
153 }
154
155 void ScrollingTreeFrameScrollingNodeIOS::updateChildNodesAfterScroll(const FloatPoint& scrollPosition)
156 {
157     ScrollBehaviorForFixedElements behaviorForFixed = scrollBehaviorForFixedElements();
158     FloatPoint scrollOffset = scrollPosition - toIntSize(scrollOrigin());
159     FloatRect viewportRect(FloatPoint(), scrollableAreaSize());
160     FloatSize scrollOffsetForFixedChildren = FrameView::scrollOffsetForFixedPosition(enclosingLayoutRect(viewportRect), roundedLayoutSize(totalContentsSize()), roundedLayoutPoint(scrollOffset), scrollOrigin(), frameScaleFactor(), false, behaviorForFixed, headerHeight(), footerHeight());
161
162     [m_counterScrollingLayer setPosition:FloatPoint(scrollOffsetForFixedChildren)];
163
164     if (m_headerLayer || m_footerLayer) {
165         // Generally the banners should have the same horizontal-position computation as a fixed element. However,
166         // the banners are not affected by the frameScaleFactor(), so if there is currently a non-1 frameScaleFactor()
167         // then we should recompute scrollOffsetForFixedChildren for the banner with a scale factor of 1.
168         float horizontalScrollOffsetForBanner = scrollOffsetForFixedChildren.width();
169         if (frameScaleFactor() != 1)
170             horizontalScrollOffsetForBanner = FrameView::scrollOffsetForFixedPosition(enclosingLayoutRect(viewportRect), roundedLayoutSize(totalContentsSize()), roundedLayoutPoint(scrollOffset), scrollOrigin(), 1, false, behaviorForFixed, headerHeight(), footerHeight()).width();
171
172         if (m_headerLayer)
173             [m_headerLayer setPosition:FloatPoint(horizontalScrollOffsetForBanner, 0)];
174
175         if (m_footerLayer)
176             [m_footerLayer setPosition:FloatPoint(horizontalScrollOffsetForBanner, totalContentsSize().height() - footerHeight())];
177     }
178     
179     if (!m_children)
180         return;
181     
182     viewportRect.setLocation(scrollOffset);
183     
184     FloatRect viewportConstrainedObjectsRect = FrameView::rectForViewportConstrainedObjects(enclosingLayoutRect(viewportRect), roundedLayoutSize(totalContentsSize()), frameScaleFactor(), false, behaviorForFixed);
185     
186     size_t size = m_children->size();
187     for (size_t i = 0; i < size; ++i)
188         m_children->at(i)->parentScrollPositionDidChange(viewportConstrainedObjectsRect, FloatSize());
189 }
190
191 FloatPoint ScrollingTreeFrameScrollingNodeIOS::minimumScrollPosition() const
192 {
193     FloatPoint position;
194     
195     if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToBottom)
196         position.setY(maximumScrollPosition().y());
197
198     return position;
199 }
200
201 FloatPoint ScrollingTreeFrameScrollingNodeIOS::maximumScrollPosition() const
202 {
203     FloatPoint position(totalContentsSizeForRubberBand().width() - scrollableAreaSize().width(),
204         totalContentsSizeForRubberBand().height() - scrollableAreaSize().height());
205
206     position = position.expandedTo(FloatPoint());
207
208     if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToTop)
209         position.setY(minimumScrollPosition().y());
210
211     return position;
212 }
213
214 void ScrollingTreeFrameScrollingNodeIOS::scrollBy(const IntSize& offset)
215 {
216     setScrollPosition(scrollPosition() + offset);
217 }
218
219 void ScrollingTreeFrameScrollingNodeIOS::scrollByWithoutContentEdgeConstraints(const IntSize& offset)
220 {
221     setScrollPositionWithoutContentEdgeConstraints(scrollPosition() + offset);
222 }
223
224 } // namespace WebCore
225
226 #endif // ENABLE(ASYNC_SCROLLING)