30f147667c9c340b3d89f504227f1844e11bf2d5
[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 Ref<ScrollingTreeFrameScrollingNodeIOS> ScrollingTreeFrameScrollingNodeIOS::create(ScrollingTree& scrollingTree, ScrollingNodeID nodeID)
44 {
45     return adoptRef(*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::commitStateBeforeChildren(const ScrollingStateNode& stateNode)
58 {
59     ScrollingTreeFrameScrollingNode::commitStateBeforeChildren(stateNode);
60     
61     const auto& scrollingStateNode = downcast<ScrollingStateFrameScrollingNode>(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::commitStateAfterChildren(const ScrollingStateNode& stateNode)
90 {
91     ScrollingTreeFrameScrollingNode::commitStateAfterChildren(stateNode);
92
93     const auto& scrollingStateNode = downcast<ScrollingStateFrameScrollingNode>(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     return -m_scrollLayer.get().position;
106 }
107
108 void ScrollingTreeFrameScrollingNodeIOS::setScrollPositionWithoutContentEdgeConstraints(const FloatPoint& scrollPosition)
109 {
110     if (shouldUpdateScrollLayerPositionSynchronously()) {
111         m_probableMainThreadScrollPosition = scrollPosition;
112         scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition, SetScrollingLayerPosition);
113         return;
114     }
115
116     setScrollLayerPosition(scrollPosition);
117     scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition);
118 }
119
120 void ScrollingTreeFrameScrollingNodeIOS::setScrollLayerPosition(const FloatPoint& scrollPosition)
121 {
122     ASSERT(!shouldUpdateScrollLayerPositionSynchronously());
123     [m_scrollLayer setPosition:-scrollPosition];
124
125     updateChildNodesAfterScroll(scrollPosition);
126 }
127
128 void ScrollingTreeFrameScrollingNodeIOS::updateLayersAfterViewportChange(const FloatRect& fixedPositionRect, double /*scale*/)
129 {
130     // Note: we never currently have a m_counterScrollingLayer (which is used for background-attachment:fixed) on iOS.
131     [m_counterScrollingLayer setPosition:fixedPositionRect.location()];
132
133     if (!m_children)
134         return;
135
136     for (auto& child : *m_children)
137         child->updateLayersAfterAncestorChange(*this, fixedPositionRect, FloatSize());
138 }
139
140 void ScrollingTreeFrameScrollingNodeIOS::updateLayersAfterDelegatedScroll(const FloatPoint& scrollPosition)
141 {
142     updateChildNodesAfterScroll(scrollPosition);
143 }
144
145 void ScrollingTreeFrameScrollingNodeIOS::updateChildNodesAfterScroll(const FloatPoint& scrollPosition)
146 {
147     ScrollBehaviorForFixedElements behaviorForFixed = scrollBehaviorForFixedElements();
148     FloatRect viewportRect(scrollPosition, scrollableAreaSize());
149     FloatPoint scrollPositionForFixedChildren = FrameView::scrollPositionForFixedPosition(enclosingLayoutRect(viewportRect), LayoutSize(totalContentsSize()), LayoutPoint(scrollPosition), scrollOrigin(), frameScaleFactor(), fixedElementsLayoutRelativeToFrame(), behaviorForFixed, headerHeight(), footerHeight());
150
151     [m_counterScrollingLayer setPosition:scrollPositionForFixedChildren];
152
153     if (m_headerLayer || m_footerLayer) {
154         // Generally the banners should have the same horizontal-position computation as a fixed element. However,
155         // the banners are not affected by the frameScaleFactor(), so if there is currently a non-1 frameScaleFactor()
156         // then we should recompute scrollPositionForFixedChildren for the banner with a scale factor of 1.
157         float horizontalScrollOffsetForBanner = scrollPositionForFixedChildren.x();
158         if (frameScaleFactor() != 1)
159             horizontalScrollOffsetForBanner = FrameView::scrollPositionForFixedPosition(enclosingLayoutRect(viewportRect), LayoutSize(totalContentsSize()), LayoutPoint(scrollPosition), scrollOrigin(), 1, fixedElementsLayoutRelativeToFrame(), behaviorForFixed, headerHeight(), footerHeight()).x();
160
161         if (m_headerLayer)
162             [m_headerLayer setPosition:FloatPoint(horizontalScrollOffsetForBanner, 0)];
163
164         if (m_footerLayer)
165             [m_footerLayer setPosition:FloatPoint(horizontalScrollOffsetForBanner, totalContentsSize().height() - footerHeight())];
166     }
167     
168     if (!m_children)
169         return;
170
171
172     FloatRect fixedPositionRect;
173     if (!parent())
174         fixedPositionRect = scrollingTree().fixedPositionRect();
175     else
176         fixedPositionRect = FloatRect(scrollPosition, scrollableAreaSize());
177
178     for (auto& child : *m_children)
179         child->updateLayersAfterAncestorChange(*this, fixedPositionRect, FloatSize());
180 }
181
182 FloatPoint ScrollingTreeFrameScrollingNodeIOS::minimumScrollPosition() const
183 {
184     FloatPoint position = ScrollableArea::scrollPositionFromOffset(FloatPoint(), toFloatSize(scrollOrigin()));
185     
186     if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToBottom)
187         position.setY(maximumScrollPosition().y());
188
189     return position;
190 }
191
192 FloatPoint ScrollingTreeFrameScrollingNodeIOS::maximumScrollPosition() const
193 {
194     FloatPoint position = ScrollableArea::scrollPositionFromOffset(FloatPoint(totalContentsSizeForRubberBand() - scrollableAreaSize()), toFloatSize(scrollOrigin()));
195     position = position.expandedTo(FloatPoint());
196
197     if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToTop)
198         position.setY(minimumScrollPosition().y());
199
200     return position;
201 }
202
203 } // namespace WebCore
204
205 #endif // ENABLE(ASYNC_SCROLLING)