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