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