Comcast website displays bottom of page when loaded
[WebKit-https.git] / Source / WebCore / page / scrolling / mac / ScrollingTreeNodeMac.mm
1 /*
2  * Copyright (C) 2012 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 #include "config.h"
27 #include "ScrollingTreeNodeMac.h"
28
29 #if ENABLE(THREADED_SCROLLING)
30
31 #include "PlatformWheelEvent.h"
32 #include "ScrollingTree.h"
33 #include "ScrollingTreeState.h"
34
35 namespace WebCore {
36
37 PassOwnPtr<ScrollingTreeNode> ScrollingTreeNode::create(ScrollingTree* scrollingTree)
38 {
39     return adoptPtr(new ScrollingTreeNodeMac(scrollingTree));
40 }
41
42 ScrollingTreeNodeMac::ScrollingTreeNodeMac(ScrollingTree* scrollingTree)
43     : ScrollingTreeNode(scrollingTree)
44     , m_scrollElasticityController(this)
45 {
46 }
47
48 ScrollingTreeNodeMac::~ScrollingTreeNodeMac()
49 {
50     if (m_snapRubberbandTimer)
51         CFRunLoopTimerInvalidate(m_snapRubberbandTimer.get());
52 }
53
54 void ScrollingTreeNodeMac::update(ScrollingTreeState* state)
55 {
56     ScrollingTreeNode::update(state);
57
58     if (state->changedProperties() & ScrollingTreeState::ScrollLayer)
59         m_scrollLayer = state->platformScrollLayer();
60
61     if (state->changedProperties() & ScrollingTreeState::RequestedScrollPosition)
62         setScrollPosition(state->requestedScrollPosition());
63
64     if (state->changedProperties() & (ScrollingTreeState::ScrollLayer | ScrollingTreeState::ContentsSize | ScrollingTreeState::ViewportRect))
65         updateMainFramePinState(scrollPosition());
66
67     if ((state->changedProperties() & ScrollingTreeState::ShouldUpdateScrollLayerPositionOnMainThread) && shouldUpdateScrollLayerPositionOnMainThread()) {
68         // We're transitioning to the slow "update scroll layer position on the main thread" mode.
69         // Initialize the probable main thread scroll position with the current scroll layer position.
70         if (state->changedProperties() & ScrollingTreeState::RequestedScrollPosition)
71             m_probableMainThreadScrollPosition = state->requestedScrollPosition();
72         else {
73             CGPoint scrollLayerPosition = m_scrollLayer.get().position;
74             m_probableMainThreadScrollPosition = IntPoint(-scrollLayerPosition.x, -scrollLayerPosition.y);
75         }
76     }
77 }
78
79 void ScrollingTreeNodeMac::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
80 {
81     if (!canHaveScrollbars())
82         return;
83
84     m_scrollElasticityController.handleWheelEvent(wheelEvent);
85     scrollingTree()->handleWheelEventPhase(wheelEvent.phase());
86 }
87
88 bool ScrollingTreeNodeMac::allowsHorizontalStretching()
89 {
90     switch (horizontalScrollElasticity()) {
91     case ScrollElasticityAutomatic:
92         return hasEnabledHorizontalScrollbar() || !hasEnabledVerticalScrollbar();
93     case ScrollElasticityNone:
94         return false;
95     case ScrollElasticityAllowed:
96         return true;
97     }
98
99     ASSERT_NOT_REACHED();
100     return false;
101 }
102
103 bool ScrollingTreeNodeMac::allowsVerticalStretching()
104 {
105     switch (verticalScrollElasticity()) {
106     case ScrollElasticityAutomatic:
107         return hasEnabledVerticalScrollbar() || !hasEnabledHorizontalScrollbar();
108     case ScrollElasticityNone:
109         return false;
110     case ScrollElasticityAllowed:
111         return true;
112     }
113
114     ASSERT_NOT_REACHED();
115     return false;
116 }
117
118 IntSize ScrollingTreeNodeMac::stretchAmount()
119 {
120     IntSize stretch;
121
122     if (scrollPosition().y() < minimumScrollPosition().y())
123         stretch.setHeight(scrollPosition().y() - minimumScrollPosition().y());
124     else if (scrollPosition().y() > maximumScrollPosition().y())
125         stretch.setHeight(scrollPosition().y() - maximumScrollPosition().y());
126
127     if (scrollPosition().x() < minimumScrollPosition().x())
128         stretch.setWidth(scrollPosition().x() - minimumScrollPosition().x());
129     else if (scrollPosition().x() > maximumScrollPosition().x())
130         stretch.setWidth(scrollPosition().x() - maximumScrollPosition().x());
131
132     return stretch;
133 }
134
135 bool ScrollingTreeNodeMac::pinnedInDirection(const FloatSize& delta)
136 {
137     FloatSize limitDelta;
138
139     if (fabsf(delta.height()) >= fabsf(delta.width())) {
140         if (delta.height() < 0) {
141             // We are trying to scroll up.  Make sure we are not pinned to the top
142             limitDelta.setHeight(scrollPosition().y() - minimumScrollPosition().y());
143         } else {
144             // We are trying to scroll down.  Make sure we are not pinned to the bottom
145             limitDelta.setHeight(maximumScrollPosition().y() - scrollPosition().y());
146         }
147     } else if (delta.width()) {
148         if (delta.width() < 0) {
149             // We are trying to scroll left.  Make sure we are not pinned to the left
150             limitDelta.setHeight(scrollPosition().x() - minimumScrollPosition().x());
151         } else {
152             // We are trying to scroll right.  Make sure we are not pinned to the right
153             limitDelta.setHeight(maximumScrollPosition().x() - scrollPosition().x());
154         }
155     }
156
157     if ((delta.width() || delta.height()) && (limitDelta.width() < 1 && limitDelta.height() < 1))        
158         return true;
159
160     return false;
161 }
162
163 bool ScrollingTreeNodeMac::canScrollHorizontally()
164 {
165     return hasEnabledHorizontalScrollbar();
166 }
167
168 bool ScrollingTreeNodeMac::canScrollVertically()
169 {
170     return hasEnabledVerticalScrollbar();
171 }
172
173 bool ScrollingTreeNodeMac::shouldRubberBandInDirection(ScrollDirection direction)
174 {
175     if (direction == ScrollLeft)
176         return !scrollingTree()->canGoBack();
177     if (direction == ScrollRight)
178         return !scrollingTree()->canGoForward();
179
180     ASSERT_NOT_REACHED();
181     return false;
182 }
183
184 IntPoint ScrollingTreeNodeMac::absoluteScrollPosition()
185 {
186     return scrollPosition();
187 }
188
189 void ScrollingTreeNodeMac::immediateScrollBy(const FloatSize& offset)
190 {
191     scrollBy(roundedIntSize(offset));
192 }
193
194 void ScrollingTreeNodeMac::immediateScrollByWithoutContentEdgeConstraints(const FloatSize& offset)
195 {
196     scrollByWithoutContentEdgeConstraints(roundedIntSize(offset));
197 }
198
199 void ScrollingTreeNodeMac::startSnapRubberbandTimer()
200 {
201     ASSERT(!m_snapRubberbandTimer);
202
203     CFTimeInterval timerInterval = 1.0 / 60.0;
204
205     m_snapRubberbandTimer = adoptCF(CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + timerInterval, timerInterval, 0, 0, ^(CFRunLoopTimerRef) {
206         m_scrollElasticityController.snapRubberBandTimerFired();
207     }));
208     CFRunLoopAddTimer(CFRunLoopGetCurrent(), m_snapRubberbandTimer.get(), kCFRunLoopDefaultMode);
209 }
210
211 void ScrollingTreeNodeMac::stopSnapRubberbandTimer()
212 {
213     if (!m_snapRubberbandTimer)
214         return;
215
216     CFRunLoopTimerInvalidate(m_snapRubberbandTimer.get());
217     m_snapRubberbandTimer = nullptr;
218 }
219
220 IntPoint ScrollingTreeNodeMac::scrollPosition() const
221 {
222     if (shouldUpdateScrollLayerPositionOnMainThread())
223         return m_probableMainThreadScrollPosition;
224
225     CGPoint scrollLayerPosition = m_scrollLayer.get().position;
226     return IntPoint(-scrollLayerPosition.x + scrollOrigin().x(), -scrollLayerPosition.y + scrollOrigin().y());
227 }
228
229 void ScrollingTreeNodeMac::setScrollPosition(const IntPoint& scrollPosition)
230 {
231     IntPoint newScrollPosition = scrollPosition;
232     newScrollPosition = newScrollPosition.shrunkTo(maximumScrollPosition());
233     newScrollPosition = newScrollPosition.expandedTo(minimumScrollPosition());
234
235     setScrollPositionWithoutContentEdgeConstraints(newScrollPosition);
236 }
237
238 void ScrollingTreeNodeMac::setScrollPositionWithoutContentEdgeConstraints(const IntPoint& scrollPosition)
239 {
240     updateMainFramePinState(scrollPosition);
241
242     if (shouldUpdateScrollLayerPositionOnMainThread()) {
243         m_probableMainThreadScrollPosition = scrollPosition;
244         scrollingTree()->updateMainFrameScrollPositionAndScrollLayerPosition(scrollPosition);
245         return;
246     }
247
248     setScrollLayerPosition(scrollPosition);
249     scrollingTree()->updateMainFrameScrollPosition(scrollPosition);
250 }
251
252 void ScrollingTreeNodeMac::setScrollLayerPosition(const IntPoint& position)
253 {
254     ASSERT(!shouldUpdateScrollLayerPositionOnMainThread());
255     m_scrollLayer.get().position = CGPointMake(-position.x() + scrollOrigin().x(), -position.y() + scrollOrigin().y());
256 }
257
258 IntPoint ScrollingTreeNodeMac::minimumScrollPosition() const
259 {
260     return IntPoint(0, 0);
261 }
262
263 IntPoint ScrollingTreeNodeMac::maximumScrollPosition() const
264 {
265     IntPoint position(contentsSize().width() - viewportRect().width(),
266                       contentsSize().height() - viewportRect().height());
267
268     position.clampNegativeToZero();
269
270     return position;
271 }
272
273 void ScrollingTreeNodeMac::scrollBy(const IntSize& offset)
274 {
275     setScrollPosition(scrollPosition() + offset);
276 }
277
278 void ScrollingTreeNodeMac::scrollByWithoutContentEdgeConstraints(const IntSize& offset)
279 {
280     setScrollPositionWithoutContentEdgeConstraints(scrollPosition() + offset);
281 }
282
283 void ScrollingTreeNodeMac::updateMainFramePinState(const IntPoint& scrollPosition)
284 {
285     bool pinnedToTheLeft = scrollPosition.x() <= minimumScrollPosition().x();
286     bool pinnedToTheRight = scrollPosition.x() >= maximumScrollPosition().x();
287
288     scrollingTree()->setMainFramePinState(pinnedToTheLeft, pinnedToTheRight);
289 }
290
291 } // namespace WebCore
292
293 #endif // ENABLE(THREADED_SCROLLING)