[iOS WK2] position:fixed in iframes with programmatic scroll could end up in the...
[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 PassRefPtr<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::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::setScrollPositionWithoutContentEdgeConstraints(const FloatPoint& scrollPosition)
110 {
111     if (shouldUpdateScrollLayerPositionSynchronously()) {
112         m_probableMainThreadScrollPosition = scrollPosition;
113         scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition, SetScrollingLayerPosition);
114         return;
115     }
116
117     setScrollLayerPosition(scrollPosition);
118     scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition);
119 }
120
121 void ScrollingTreeFrameScrollingNodeIOS::setScrollLayerPosition(const FloatPoint& scrollPosition)
122 {
123     ASSERT(!shouldUpdateScrollLayerPositionSynchronously());
124     [m_scrollLayer setPosition:CGPointMake(-scrollPosition.x() + scrollOrigin().x(), -scrollPosition.y() + scrollOrigin().y())];
125
126     updateChildNodesAfterScroll(scrollPosition);
127 }
128
129 void ScrollingTreeFrameScrollingNodeIOS::updateLayersAfterViewportChange(const FloatRect& fixedPositionRect, double /*scale*/)
130 {
131     // Note: we never currently have a m_counterScrollingLayer (which is used for background-attachment:fixed) on iOS.
132     [m_counterScrollingLayer setPosition:fixedPositionRect.location()];
133
134     if (!m_children)
135         return;
136
137     for (auto& child : *m_children)
138         child->updateLayersAfterAncestorChange(*this, fixedPositionRect, FloatSize());
139 }
140
141 void ScrollingTreeFrameScrollingNodeIOS::updateLayersAfterDelegatedScroll(const FloatPoint& scrollPosition)
142 {
143     updateChildNodesAfterScroll(scrollPosition);
144 }
145
146 void ScrollingTreeFrameScrollingNodeIOS::updateChildNodesAfterScroll(const FloatPoint& scrollPosition)
147 {
148     ScrollBehaviorForFixedElements behaviorForFixed = scrollBehaviorForFixedElements();
149     FloatPoint scrollOffset = scrollPosition - toIntSize(scrollOrigin());
150     FloatRect viewportRect(FloatPoint(), scrollableAreaSize());
151     FloatSize scrollOffsetForFixedChildren = FrameView::scrollOffsetForFixedPosition(enclosingLayoutRect(viewportRect), roundedLayoutSize(totalContentsSize()), roundedLayoutPoint(scrollOffset), scrollOrigin(), frameScaleFactor(), false, behaviorForFixed, headerHeight(), footerHeight());
152
153     [m_counterScrollingLayer setPosition:FloatPoint(scrollOffsetForFixedChildren)];
154
155     if (m_headerLayer || m_footerLayer) {
156         // Generally the banners should have the same horizontal-position computation as a fixed element. However,
157         // the banners are not affected by the frameScaleFactor(), so if there is currently a non-1 frameScaleFactor()
158         // then we should recompute scrollOffsetForFixedChildren for the banner with a scale factor of 1.
159         float horizontalScrollOffsetForBanner = scrollOffsetForFixedChildren.width();
160         if (frameScaleFactor() != 1)
161             horizontalScrollOffsetForBanner = FrameView::scrollOffsetForFixedPosition(enclosingLayoutRect(viewportRect), roundedLayoutSize(totalContentsSize()), roundedLayoutPoint(scrollOffset), scrollOrigin(), 1, false, behaviorForFixed, headerHeight(), footerHeight()).width();
162
163         if (m_headerLayer)
164             [m_headerLayer setPosition:FloatPoint(horizontalScrollOffsetForBanner, 0)];
165
166         if (m_footerLayer)
167             [m_footerLayer setPosition:FloatPoint(horizontalScrollOffsetForBanner, totalContentsSize().height() - footerHeight())];
168     }
169     
170     if (!m_children)
171         return;
172
173
174     FloatRect fixedPositionRect;
175     if (!parent())
176         fixedPositionRect = scrollingTree().fixedPositionRect();
177     else
178         fixedPositionRect = FloatRect(scrollOffset, scrollableAreaSize());
179
180     for (auto& child : *m_children)
181         child->updateLayersAfterAncestorChange(*this, fixedPositionRect, FloatSize());
182 }
183
184 FloatPoint ScrollingTreeFrameScrollingNodeIOS::minimumScrollPosition() const
185 {
186     FloatPoint position;
187     
188     if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToBottom)
189         position.setY(maximumScrollPosition().y());
190
191     return position;
192 }
193
194 FloatPoint ScrollingTreeFrameScrollingNodeIOS::maximumScrollPosition() const
195 {
196     FloatPoint position(totalContentsSizeForRubberBand().width() - scrollableAreaSize().width(),
197         totalContentsSizeForRubberBand().height() - scrollableAreaSize().height());
198
199     position = position.expandedTo(FloatPoint());
200
201     if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToTop)
202         position.setY(minimumScrollPosition().y());
203
204     return position;
205 }
206
207 } // namespace WebCore
208
209 #endif // ENABLE(ASYNC_SCROLLING)