[WTF] Import std::optional reference implementation as WTF::Optional
[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, std::nullopt, ScrollingLayerPositionAction::Set);
113         return;
114     }
115
116     FloatRect layoutViewport; // FIXME: implement for iOS WK1.
117     setScrollLayerPosition(scrollPosition, layoutViewport);
118     scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition, std::nullopt);
119 }
120
121 void ScrollingTreeFrameScrollingNodeIOS::setScrollLayerPosition(const FloatPoint& scrollPosition, const FloatRect&)
122 {
123     ASSERT(!shouldUpdateScrollLayerPositionSynchronously());
124     [m_scrollLayer setPosition:-scrollPosition];
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     FloatRect viewportRect(scrollPosition, scrollableAreaSize());
150     FloatPoint scrollPositionForFixedChildren = FrameView::scrollPositionForFixedPosition(enclosingLayoutRect(viewportRect), LayoutSize(totalContentsSize()), LayoutPoint(scrollPosition), scrollOrigin(), frameScaleFactor(), fixedElementsLayoutRelativeToFrame(), behaviorForFixed, headerHeight(), footerHeight());
151
152     [m_counterScrollingLayer setPosition:scrollPositionForFixedChildren];
153
154     if (m_headerLayer || m_footerLayer) {
155         // Generally the banners should have the same horizontal-position computation as a fixed element. However,
156         // the banners are not affected by the frameScaleFactor(), so if there is currently a non-1 frameScaleFactor()
157         // then we should recompute scrollPositionForFixedChildren for the banner with a scale factor of 1.
158         float horizontalScrollOffsetForBanner = scrollPositionForFixedChildren.x();
159         if (frameScaleFactor() != 1)
160             horizontalScrollOffsetForBanner = FrameView::scrollPositionForFixedPosition(enclosingLayoutRect(viewportRect), LayoutSize(totalContentsSize()), LayoutPoint(scrollPosition), scrollOrigin(), 1, fixedElementsLayoutRelativeToFrame(), behaviorForFixed, headerHeight(), footerHeight()).x();
161
162         if (m_headerLayer)
163             [m_headerLayer setPosition:FloatPoint(horizontalScrollOffsetForBanner, 0)];
164
165         if (m_footerLayer)
166             [m_footerLayer setPosition:FloatPoint(horizontalScrollOffsetForBanner, totalContentsSize().height() - footerHeight())];
167     }
168     
169     if (!m_children)
170         return;
171
172
173     FloatRect fixedPositionRect;
174     if (!parent())
175         fixedPositionRect = scrollingTree().fixedPositionRect();
176     else
177         fixedPositionRect = FloatRect(scrollPosition, scrollableAreaSize());
178
179     for (auto& child : *m_children)
180         child->updateLayersAfterAncestorChange(*this, fixedPositionRect, FloatSize());
181 }
182
183 FloatPoint ScrollingTreeFrameScrollingNodeIOS::minimumScrollPosition() const
184 {
185     FloatPoint position = ScrollableArea::scrollPositionFromOffset(FloatPoint(), toFloatSize(scrollOrigin()));
186     
187     if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToBottom)
188         position.setY(maximumScrollPosition().y());
189
190     return position;
191 }
192
193 FloatPoint ScrollingTreeFrameScrollingNodeIOS::maximumScrollPosition() const
194 {
195     FloatPoint position = ScrollableArea::scrollPositionFromOffset(FloatPoint(totalContentsSizeForRubberBand() - scrollableAreaSize()), toFloatSize(scrollOrigin()));
196     position = position.expandedTo(FloatPoint());
197
198     if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToTop)
199         position.setY(minimumScrollPosition().y());
200
201     return position;
202 }
203
204 } // namespace WebCore
205
206 #endif // ENABLE(ASYNC_SCROLLING)