Fix reload and programmatic scrolling in RTL documents
[WebKit-https.git] / Source / WebCore / page / scrolling / mac / ScrollingTreeFrameScrollingNodeMac.mm
1 /*
2  * Copyright (C) 2012, 2014-2015 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 "ScrollingTreeFrameScrollingNodeMac.h"
28
29 #if ENABLE(ASYNC_SCROLLING) && PLATFORM(MAC)
30
31 #import "FrameView.h"
32 #import "LayoutSize.h"
33 #import "Logging.h"
34 #import "NSScrollerImpDetails.h"
35 #import "PlatformWheelEvent.h"
36 #import "ScrollableArea.h"
37 #import "ScrollingCoordinator.h"
38 #import "ScrollingTree.h"
39 #import "ScrollingStateTree.h"
40 #import "Settings.h"
41 #import "TextStream.h"
42 #import "TileController.h"
43 #import "WebLayer.h"
44
45 #import <QuartzCore/QuartzCore.h>
46 #import <wtf/CurrentTime.h>
47 #import <wtf/Deque.h>
48 #import <wtf/text/StringBuilder.h>
49 #import <wtf/text/CString.h>
50
51 namespace WebCore {
52
53 static void logThreadedScrollingMode(unsigned synchronousScrollingReasons);
54
55 Ref<ScrollingTreeFrameScrollingNode> ScrollingTreeFrameScrollingNodeMac::create(ScrollingTree& scrollingTree, ScrollingNodeID nodeID)
56 {
57     return adoptRef(*new ScrollingTreeFrameScrollingNodeMac(scrollingTree, nodeID));
58 }
59
60 ScrollingTreeFrameScrollingNodeMac::ScrollingTreeFrameScrollingNodeMac(ScrollingTree& scrollingTree, ScrollingNodeID nodeID)
61     : ScrollingTreeFrameScrollingNode(scrollingTree, nodeID)
62     , m_scrollController(*this)
63     , m_verticalScrollbarPainter(nullptr)
64     , m_horizontalScrollbarPainter(nullptr)
65 {
66 }
67
68 ScrollingTreeFrameScrollingNodeMac::~ScrollingTreeFrameScrollingNodeMac()
69 {
70 }
71
72 #if ENABLE(CSS_SCROLL_SNAP)
73 static inline Vector<LayoutUnit> convertToLayoutUnits(const Vector<float>& snapOffsetsAsFloat)
74 {
75     Vector<LayoutUnit> snapOffsets;
76     snapOffsets.reserveInitialCapacity(snapOffsetsAsFloat.size());
77     for (auto offset : snapOffsetsAsFloat)
78         snapOffsets.append(offset);
79
80     return snapOffsets;
81 }
82 #endif
83
84 void ScrollingTreeFrameScrollingNodeMac::updateBeforeChildren(const ScrollingStateNode& stateNode)
85 {
86     ScrollingTreeFrameScrollingNode::updateBeforeChildren(stateNode);
87     const auto& scrollingStateNode = downcast<ScrollingStateFrameScrollingNode>(stateNode);
88
89     if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::ScrollLayer))
90         m_scrollLayer = scrollingStateNode.layer();
91
92     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::ScrolledContentsLayer))
93         m_scrolledContentsLayer = scrollingStateNode.scrolledContentsLayer();
94
95     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::CounterScrollingLayer))
96         m_counterScrollingLayer = scrollingStateNode.counterScrollingLayer();
97
98     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::InsetClipLayer))
99         m_insetClipLayer = scrollingStateNode.insetClipLayer();
100
101     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::ContentShadowLayer))
102         m_contentShadowLayer = scrollingStateNode.contentShadowLayer();
103
104     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::HeaderLayer))
105         m_headerLayer = scrollingStateNode.headerLayer();
106
107     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::FooterLayer))
108         m_footerLayer = scrollingStateNode.footerLayer();
109
110     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::PainterForScrollbar)) {
111         m_verticalScrollbarPainter = scrollingStateNode.verticalScrollbarPainter();
112         m_horizontalScrollbarPainter = scrollingStateNode.horizontalScrollbarPainter();
113     }
114
115     bool logScrollingMode = !m_hadFirstUpdate;
116     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::ReasonsForSynchronousScrolling)) {
117         if (shouldUpdateScrollLayerPositionSynchronously()) {
118             // We're transitioning to the slow "update scroll layer position on the main thread" mode.
119             // Initialize the probable main thread scroll position with the current scroll layer position.
120             if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::RequestedScrollPosition))
121                 m_probableMainThreadScrollPosition = scrollingStateNode.requestedScrollPosition();
122             else {
123                 CGPoint scrollLayerPosition = m_scrollLayer.get().position;
124                 m_probableMainThreadScrollPosition = FloatPoint(-scrollLayerPosition.x, -scrollLayerPosition.y);
125             }
126         }
127
128         logScrollingMode = true;
129     }
130
131     if (logScrollingMode) {
132         if (scrollingTree().scrollingPerformanceLoggingEnabled())
133             logThreadedScrollingMode(synchronousScrollingReasons());
134     }
135
136 #if ENABLE(CSS_SCROLL_SNAP)
137     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::HorizontalSnapOffsets))
138         m_scrollController.updateScrollSnapPoints(ScrollEventAxis::Horizontal, convertToLayoutUnits(scrollingStateNode.horizontalSnapOffsets()));
139
140     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::VerticalSnapOffsets))
141         m_scrollController.updateScrollSnapPoints(ScrollEventAxis::Vertical, convertToLayoutUnits(scrollingStateNode.verticalSnapOffsets()));
142
143     if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::CurrentHorizontalSnapOffsetIndex))
144         m_scrollController.setActiveScrollSnapIndexForAxis(ScrollEventAxis::Horizontal, scrollingStateNode.currentHorizontalSnapPointIndex());
145     
146     if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::CurrentVerticalSnapOffsetIndex))
147         m_scrollController.setActiveScrollSnapIndexForAxis(ScrollEventAxis::Vertical, scrollingStateNode.currentVerticalSnapPointIndex());
148 #endif
149
150     if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ExpectsWheelEventTestTrigger))
151         m_expectsWheelEventTestTrigger = scrollingStateNode.expectsWheelEventTestTrigger();
152
153     m_hadFirstUpdate = true;
154 }
155
156 void ScrollingTreeFrameScrollingNodeMac::updateAfterChildren(const ScrollingStateNode& stateNode)
157 {
158     ScrollingTreeFrameScrollingNode::updateAfterChildren(stateNode);
159
160     const auto& scrollingStateNode = downcast<ScrollingStateScrollingNode>(stateNode);
161
162     // Update the scroll position after child nodes have been updated, because they need to have updated their constraints before any scrolling happens.
163     if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::RequestedScrollPosition))
164         setScrollPosition(scrollingStateNode.requestedScrollPosition());
165
166     if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::ScrollLayer)
167         || scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::TotalContentsSize)
168         || scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ScrollableAreaSize))
169         updateMainFramePinState(scrollPosition());
170 }
171
172 void ScrollingTreeFrameScrollingNodeMac::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
173 {
174     if (!canHaveScrollbars())
175         return;
176
177     if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseBegan) {
178         [m_verticalScrollbarPainter setUsePresentationValue:YES];
179         [m_horizontalScrollbarPainter setUsePresentationValue:YES];
180     }
181     if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded || wheelEvent.momentumPhase() == PlatformWheelEventPhaseCancelled) {
182         [m_verticalScrollbarPainter setUsePresentationValue:NO];
183         [m_horizontalScrollbarPainter setUsePresentationValue:NO];
184     }
185
186 #if ENABLE(CSS_SCROLL_SNAP) || ENABLE(RUBBER_BANDING)
187     if (m_expectsWheelEventTestTrigger) {
188         if (scrollingTree().shouldHandleWheelEventSynchronously(wheelEvent))
189             removeTestDeferralForReason(reinterpret_cast<WheelEventTestTrigger::ScrollableAreaIdentifier>(scrollingNodeID()), WheelEventTestTrigger::ScrollingThreadSyncNeeded);
190         else
191             deferTestsForReason(reinterpret_cast<WheelEventTestTrigger::ScrollableAreaIdentifier>(scrollingNodeID()), WheelEventTestTrigger::ScrollingThreadSyncNeeded);
192     }
193 #endif
194
195     m_scrollController.handleWheelEvent(wheelEvent);
196 #if ENABLE(CSS_SCROLL_SNAP)
197     scrollingTree().setMainFrameIsScrollSnapping(m_scrollController.isScrollSnapInProgress());
198     if (m_scrollController.activeScrollSnapIndexDidChange())
199         scrollingTree().setActiveScrollSnapIndices(scrollingNodeID(), m_scrollController.activeScrollSnapIndexForAxis(ScrollEventAxis::Horizontal), m_scrollController.activeScrollSnapIndexForAxis(ScrollEventAxis::Vertical));
200 #endif
201     scrollingTree().setOrClearLatchedNode(wheelEvent, scrollingNodeID());
202     scrollingTree().handleWheelEventPhase(wheelEvent.phase());
203 }
204
205 // FIXME: We should find a way to share some of the code from newGestureIsStarting(), isAlreadyPinnedInDirectionOfGesture(),
206 // allowsVerticalStretching(), and allowsHorizontalStretching() with the implementation in ScrollAnimatorMac.
207 static bool newGestureIsStarting(const PlatformWheelEvent& wheelEvent)
208 {
209     return wheelEvent.phase() == PlatformWheelEventPhaseMayBegin || wheelEvent.phase() == PlatformWheelEventPhaseBegan;
210 }
211
212 bool ScrollingTreeFrameScrollingNodeMac::isAlreadyPinnedInDirectionOfGesture(const PlatformWheelEvent& wheelEvent, ScrollEventAxis axis)
213 {
214     switch (axis) {
215     case ScrollEventAxis::Vertical:
216         return (wheelEvent.deltaY() > 0 && scrollPosition().y() <= minimumScrollPosition().y()) || (wheelEvent.deltaY() < 0 && scrollPosition().y() >= maximumScrollPosition().y());
217     case ScrollEventAxis::Horizontal:
218         return (wheelEvent.deltaX() > 0 && scrollPosition().x() <= minimumScrollPosition().x()) || (wheelEvent.deltaX() < 0 && scrollPosition().x() >= maximumScrollPosition().x());
219     }
220
221     ASSERT_NOT_REACHED();
222     return false;
223 }
224
225 bool ScrollingTreeFrameScrollingNodeMac::allowsHorizontalStretching(const PlatformWheelEvent& wheelEvent)
226 {
227     switch (horizontalScrollElasticity()) {
228     case ScrollElasticityAutomatic: {
229         bool scrollbarsAllowStretching = hasEnabledHorizontalScrollbar() || !hasEnabledVerticalScrollbar();
230         bool eventPreventsStretching = newGestureIsStarting(wheelEvent) && isAlreadyPinnedInDirectionOfGesture(wheelEvent, ScrollEventAxis::Horizontal);
231         return scrollbarsAllowStretching && !eventPreventsStretching;
232     }
233     case ScrollElasticityNone:
234         return false;
235     case ScrollElasticityAllowed:
236         return true;
237     }
238
239     ASSERT_NOT_REACHED();
240     return false;
241 }
242
243 bool ScrollingTreeFrameScrollingNodeMac::allowsVerticalStretching(const PlatformWheelEvent& wheelEvent)
244 {
245     switch (verticalScrollElasticity()) {
246     case ScrollElasticityAutomatic: {
247         bool scrollbarsAllowStretching = hasEnabledVerticalScrollbar() || !hasEnabledHorizontalScrollbar();
248         bool eventPreventsStretching = newGestureIsStarting(wheelEvent) && isAlreadyPinnedInDirectionOfGesture(wheelEvent, ScrollEventAxis::Vertical);
249         return scrollbarsAllowStretching && !eventPreventsStretching;
250     }
251     case ScrollElasticityNone:
252         return false;
253     case ScrollElasticityAllowed:
254         return true;
255     }
256
257     ASSERT_NOT_REACHED();
258     return false;
259 }
260
261 IntSize ScrollingTreeFrameScrollingNodeMac::stretchAmount()
262 {
263     IntSize stretch;
264
265     if (scrollPosition().y() < minimumScrollPosition().y())
266         stretch.setHeight(scrollPosition().y() - minimumScrollPosition().y());
267     else if (scrollPosition().y() > maximumScrollPosition().y())
268         stretch.setHeight(scrollPosition().y() - maximumScrollPosition().y());
269
270     if (scrollPosition().x() < minimumScrollPosition().x())
271         stretch.setWidth(scrollPosition().x() - minimumScrollPosition().x());
272     else if (scrollPosition().x() > maximumScrollPosition().x())
273         stretch.setWidth(scrollPosition().x() - maximumScrollPosition().x());
274
275     if (scrollingTree().rootNode() == this) {
276         if (stretch.isZero())
277             scrollingTree().setMainFrameIsRubberBanding(false);
278         else
279             scrollingTree().setMainFrameIsRubberBanding(true);
280     }
281
282     return stretch;
283 }
284
285 bool ScrollingTreeFrameScrollingNodeMac::pinnedInDirection(const FloatSize& delta)
286 {
287     FloatSize limitDelta;
288
289     if (fabsf(delta.height()) >= fabsf(delta.width())) {
290         if (delta.height() < 0) {
291             // We are trying to scroll up. Make sure we are not pinned to the top.
292             limitDelta.setHeight(scrollPosition().y() - minimumScrollPosition().y());
293         } else {
294             // We are trying to scroll down. Make sure we are not pinned to the bottom.
295             limitDelta.setHeight(maximumScrollPosition().y() - scrollPosition().y());
296         }
297     } else if (delta.width()) {
298         if (delta.width() < 0) {
299             // We are trying to scroll left. Make sure we are not pinned to the left.
300             limitDelta.setWidth(scrollPosition().x() - minimumScrollPosition().x());
301         } else {
302             // We are trying to scroll right. Make sure we are not pinned to the right.
303             limitDelta.setWidth(maximumScrollPosition().x() - scrollPosition().x());
304         }
305     }
306
307     if ((delta.width() || delta.height()) && (limitDelta.width() < 1 && limitDelta.height() < 1))
308         return true;
309
310     return false;
311 }
312
313 bool ScrollingTreeFrameScrollingNodeMac::canScrollHorizontally()
314 {
315     return hasEnabledHorizontalScrollbar();
316 }
317
318 bool ScrollingTreeFrameScrollingNodeMac::canScrollVertically()
319 {
320     return hasEnabledVerticalScrollbar();
321 }
322
323 bool ScrollingTreeFrameScrollingNodeMac::shouldRubberBandInDirection(ScrollDirection)
324 {
325     return true;
326 }
327
328 IntPoint ScrollingTreeFrameScrollingNodeMac::absoluteScrollPosition()
329 {
330     return roundedIntPoint(scrollPosition());
331 }
332
333 void ScrollingTreeFrameScrollingNodeMac::immediateScrollBy(const FloatSize& delta)
334 {
335     scrollBy(delta);
336 }
337
338 void ScrollingTreeFrameScrollingNodeMac::immediateScrollByWithoutContentEdgeConstraints(const FloatSize& offset)
339 {
340     scrollByWithoutContentEdgeConstraints(offset);
341 }
342
343 void ScrollingTreeFrameScrollingNodeMac::stopSnapRubberbandTimer()
344 {
345     scrollingTree().setMainFrameIsRubberBanding(false);
346
347     // Since the rubberband timer has stopped, totalContentsSizeForRubberBand can be synchronized with totalContentsSize.
348     setTotalContentsSizeForRubberBand(totalContentsSize());
349 }
350
351 void ScrollingTreeFrameScrollingNodeMac::adjustScrollPositionToBoundsIfNecessary()
352 {
353     FloatPoint currentScrollPosition = absoluteScrollPosition();
354     FloatPoint minPosition = minimumScrollPosition();
355     FloatPoint maxPosition = maximumScrollPosition();
356
357     float nearestXWithinBounds = std::max(std::min(currentScrollPosition.x(), maxPosition.x()), minPosition.x());
358     float nearestYWithinBounds = std::max(std::min(currentScrollPosition.y(), maxPosition.y()), minPosition.y());
359
360     FloatPoint nearestPointWithinBounds(nearestXWithinBounds, nearestYWithinBounds);
361     immediateScrollBy(nearestPointWithinBounds - currentScrollPosition);
362 }
363
364 FloatPoint ScrollingTreeFrameScrollingNodeMac::scrollPosition() const
365 {
366     if (shouldUpdateScrollLayerPositionSynchronously())
367         return m_probableMainThreadScrollPosition;
368
369     return -m_scrollLayer.get().position;
370 }
371
372 void ScrollingTreeFrameScrollingNodeMac::setScrollPosition(const FloatPoint& scrollPosition)
373 {
374     LOG_WITH_STREAM(Scrolling, stream << "ScrollingTreeFrameScrollingNodeMac::setScrollPosition " << scrollPosition << " scrollPosition(): " << this->scrollPosition() << " min: " << minimumScrollPosition() << " max: " << maximumScrollPosition());
375
376     // Scroll deltas can be non-integral with some input devices, so scrollPosition may not be integral.
377     // FIXME: when we support half-pixel scroll positions on Retina displays, this will need to round to half pixels.
378     FloatPoint roundedPosition(roundf(scrollPosition.x()), roundf(scrollPosition.y()));
379
380     ScrollingTreeFrameScrollingNode::setScrollPosition(roundedPosition);
381
382     if (scrollingTree().scrollingPerformanceLoggingEnabled())
383         logExposedUnfilledArea();
384 }
385
386 void ScrollingTreeFrameScrollingNodeMac::setScrollPositionWithoutContentEdgeConstraints(const FloatPoint& scrollPosition)
387 {
388     updateMainFramePinState(scrollPosition);
389
390     if (shouldUpdateScrollLayerPositionSynchronously()) {
391         m_probableMainThreadScrollPosition = scrollPosition;
392         scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition, SetScrollingLayerPosition);
393         return;
394     }
395
396     setScrollLayerPosition(scrollPosition);
397     scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition);
398 }
399
400 void ScrollingTreeFrameScrollingNodeMac::setScrollLayerPosition(const FloatPoint& position)
401 {
402     ASSERT(!shouldUpdateScrollLayerPositionSynchronously());
403
404     m_scrollLayer.get().position = -position;
405
406     ScrollBehaviorForFixedElements behaviorForFixed = scrollBehaviorForFixedElements();
407     FloatRect viewportRect(position, scrollableAreaSize());
408     
409     FloatPoint scrollPositionForFixedChildren = FrameView::scrollPositionForFixedPosition(enclosingLayoutRect(viewportRect), LayoutSize(totalContentsSize()), LayoutPoint(position), scrollOrigin(), frameScaleFactor(),
410         fixedElementsLayoutRelativeToFrame(), behaviorForFixed, headerHeight(), footerHeight());
411     
412     if (m_counterScrollingLayer)
413         m_counterScrollingLayer.get().position = scrollPositionForFixedChildren;
414
415     float topContentInset = this->topContentInset();
416     if (m_insetClipLayer && m_scrolledContentsLayer && topContentInset) {
417         m_insetClipLayer.get().position = FloatPoint(0, FrameView::yPositionForInsetClipLayer(position, topContentInset));
418         m_scrolledContentsLayer.get().position = FloatPoint(m_scrolledContentsLayer.get().position.x,
419             FrameView::yPositionForRootContentLayer(position, topContentInset, headerHeight()));
420         if (m_contentShadowLayer)
421             m_contentShadowLayer.get().position = m_scrolledContentsLayer.get().position;
422     }
423
424     if (m_headerLayer || m_footerLayer) {
425         // Generally the banners should have the same horizontal-position computation as a fixed element. However,
426         // the banners are not affected by the frameScaleFactor(), so if there is currently a non-1 frameScaleFactor()
427         // then we should recompute scrollOffsetForFixedChildren for the banner with a scale factor of 1.
428         float horizontalScrollOffsetForBanner = scrollPositionForFixedChildren.x();
429         if (frameScaleFactor() != 1)
430             horizontalScrollOffsetForBanner = FrameView::scrollPositionForFixedPosition(enclosingLayoutRect(viewportRect), LayoutSize(totalContentsSize()), LayoutPoint(position), scrollOrigin(), 1, fixedElementsLayoutRelativeToFrame(), behaviorForFixed, headerHeight(), footerHeight()).x();
431
432         if (m_headerLayer)
433             m_headerLayer.get().position = FloatPoint(horizontalScrollOffsetForBanner, FrameView::yPositionForHeaderLayer(position, topContentInset));
434
435         if (m_footerLayer) {
436             m_footerLayer.get().position = FloatPoint(horizontalScrollOffsetForBanner,
437                 FrameView::yPositionForFooterLayer(position, topContentInset, totalContentsSize().height(), footerHeight()));
438         }
439     }
440
441     if (m_verticalScrollbarPainter || m_horizontalScrollbarPainter) {
442         [CATransaction begin];
443         [CATransaction lock];
444
445         if ([m_verticalScrollbarPainter shouldUsePresentationValue]) {
446             float presentationValue;
447             float overhangAmount;
448             ScrollableArea::computeScrollbarValueAndOverhang(position.y(), totalContentsSize().height(), viewportRect.height(), presentationValue, overhangAmount);
449             [m_verticalScrollbarPainter setPresentationValue:presentationValue];
450         }
451
452         if ([m_horizontalScrollbarPainter shouldUsePresentationValue]) {
453             float presentationValue;
454             float overhangAmount;
455             ScrollableArea::computeScrollbarValueAndOverhang(position.x(), totalContentsSize().width(), viewportRect.width(), presentationValue, overhangAmount);
456             [m_horizontalScrollbarPainter setPresentationValue:presentationValue];
457         }
458         [CATransaction unlock];
459         [CATransaction commit];
460     }
461
462     if (!m_children)
463         return;
464
465     viewportRect.setLocation(scrollPositionForFixedChildren);
466
467     for (auto& child : *m_children)
468         child->updateLayersAfterAncestorChange(*this, viewportRect, FloatSize());
469 }
470
471 void ScrollingTreeFrameScrollingNodeMac::updateLayersAfterViewportChange(const FloatRect&, double)
472 {
473     ASSERT_NOT_REACHED();
474 }
475
476 FloatPoint ScrollingTreeFrameScrollingNodeMac::minimumScrollPosition() const
477 {
478     FloatPoint position = ScrollableArea::scrollPositionFromOffset(FloatPoint(), toFloatSize(scrollOrigin()));
479     
480     if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToBottom)
481         position.setY(maximumScrollPosition().y());
482
483     return position;
484 }
485
486 FloatPoint ScrollingTreeFrameScrollingNodeMac::maximumScrollPosition() const
487 {
488     FloatPoint position = ScrollableArea::scrollPositionFromOffset(FloatPoint(totalContentsSizeForRubberBand() - scrollableAreaSize()), toFloatSize(scrollOrigin()));
489     position = position.expandedTo(FloatPoint());
490
491     if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToTop)
492         position.setY(minimumScrollPosition().y());
493
494     return position;
495 }
496
497 void ScrollingTreeFrameScrollingNodeMac::updateMainFramePinState(const FloatPoint& scrollPosition)
498 {
499     bool pinnedToTheLeft = scrollPosition.x() <= minimumScrollPosition().x();
500     bool pinnedToTheRight = scrollPosition.x() >= maximumScrollPosition().x();
501     bool pinnedToTheTop = scrollPosition.y() <= minimumScrollPosition().y();
502     bool pinnedToTheBottom = scrollPosition.y() >= maximumScrollPosition().y();
503
504     scrollingTree().setMainFramePinState(pinnedToTheLeft, pinnedToTheRight, pinnedToTheTop, pinnedToTheBottom);
505 }
506
507 void ScrollingTreeFrameScrollingNodeMac::logExposedUnfilledArea()
508 {
509     Region paintedVisibleTiles;
510
511     Deque<CALayer*> layerQueue;
512     layerQueue.append(m_scrollLayer.get());
513     PlatformLayerList tiles;
514
515     while (!layerQueue.isEmpty() && tiles.isEmpty()) {
516         CALayer* layer = layerQueue.takeFirst();
517         NSArray* sublayers = [[layer sublayers] copy];
518
519         // If this layer is the parent of a tile, it is the parent of all of the tiles and nothing else.
520         if ([[[sublayers objectAtIndex:0] valueForKey:@"isTile"] boolValue]) {
521             for (CALayer* sublayer in sublayers)
522                 tiles.append(sublayer);
523         } else {
524             for (CALayer* sublayer in sublayers)
525                 layerQueue.append(sublayer);
526         }
527
528         [sublayers release];
529     }
530
531     FloatPoint scrollPosition = this->scrollPosition();
532     FloatRect viewPortRect(FloatPoint(), scrollableAreaSize());
533     unsigned unfilledArea = TileController::blankPixelCountForTiles(tiles, viewPortRect, IntPoint(-scrollPosition.x(), -scrollPosition.y()));
534
535     if (unfilledArea || m_lastScrollHadUnfilledPixels)
536         WTFLogAlways("SCROLLING: Exposed tileless area. Time: %f Unfilled Pixels: %u\n", WTF::monotonicallyIncreasingTime(), unfilledArea);
537
538     m_lastScrollHadUnfilledPixels = unfilledArea;
539 }
540
541 static void logThreadedScrollingMode(unsigned synchronousScrollingReasons)
542 {
543     if (synchronousScrollingReasons) {
544         StringBuilder reasonsDescription;
545
546         if (synchronousScrollingReasons & ScrollingCoordinator::ForcedOnMainThread)
547             reasonsDescription.appendLiteral("forced,");
548         if (synchronousScrollingReasons & ScrollingCoordinator::HasSlowRepaintObjects)
549             reasonsDescription.appendLiteral("slow-repaint objects,");
550         if (synchronousScrollingReasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers)
551             reasonsDescription.appendLiteral("viewport-constrained objects,");
552         if (synchronousScrollingReasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects)
553             reasonsDescription.appendLiteral("non-layer viewport-constrained objects,");
554         if (synchronousScrollingReasons & ScrollingCoordinator::IsImageDocument)
555             reasonsDescription.appendLiteral("image document,");
556
557         // Strip the trailing comma.
558         String reasonsDescriptionTrimmed = reasonsDescription.toString().left(reasonsDescription.length() - 1);
559
560         WTFLogAlways("SCROLLING: Switching to main-thread scrolling mode. Time: %f Reason(s): %s\n", WTF::monotonicallyIncreasingTime(), reasonsDescriptionTrimmed.ascii().data());
561     } else
562         WTFLogAlways("SCROLLING: Switching to threaded scrolling mode. Time: %f\n", WTF::monotonicallyIncreasingTime());
563 }
564
565 #if ENABLE(CSS_SCROLL_SNAP)
566 LayoutUnit ScrollingTreeFrameScrollingNodeMac::scrollOffsetOnAxis(ScrollEventAxis axis) const
567 {
568     const FloatPoint& currentPosition = scrollPosition();
569     return axis == ScrollEventAxis::Horizontal ? currentPosition.x() : currentPosition.y();
570 }
571
572 void ScrollingTreeFrameScrollingNodeMac::immediateScrollOnAxis(ScrollEventAxis axis, float delta)
573 {
574     const FloatPoint& currentPosition = scrollPosition();
575     FloatPoint change;
576     if (axis == ScrollEventAxis::Horizontal)
577         change = FloatPoint(currentPosition.x() + delta, currentPosition.y());
578     else
579         change = FloatPoint(currentPosition.x(), currentPosition.y() + delta);
580
581     immediateScrollBy(change - currentPosition);
582 }
583
584 float ScrollingTreeFrameScrollingNodeMac::pageScaleFactor() const
585 {
586     return frameScaleFactor();
587 }
588
589 void ScrollingTreeFrameScrollingNodeMac::startScrollSnapTimer()
590 {
591     scrollingTree().setMainFrameIsScrollSnapping(true);
592 }
593
594 void ScrollingTreeFrameScrollingNodeMac::stopScrollSnapTimer()
595 {
596     scrollingTree().setMainFrameIsScrollSnapping(false);
597 }
598     
599 LayoutSize ScrollingTreeFrameScrollingNodeMac::scrollExtent() const
600 {
601     return LayoutSize(totalContentsSize());
602 }
603 #endif
604
605 void ScrollingTreeFrameScrollingNodeMac::deferTestsForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) const
606 {
607     if (!m_expectsWheelEventTestTrigger)
608         return;
609
610     LOG(WheelEventTestTriggers, "  ScrollingTreeFrameScrollingNodeMac::deferTestsForReason: STARTING deferral for %p because of %d", identifier, reason);
611     scrollingTree().deferTestsForReason(identifier, reason);
612 }
613     
614 void ScrollingTreeFrameScrollingNodeMac::removeTestDeferralForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) const
615 {
616     if (!m_expectsWheelEventTestTrigger)
617         return;
618     
619     LOG(WheelEventTestTriggers, "   ScrollingTreeFrameScrollingNodeMac::deferTestsForReason: ENDING deferral for %p because of %d", identifier, reason);
620     scrollingTree().removeTestDeferralForReason(identifier, reason);
621 }
622
623 } // namespace WebCore
624
625 #endif // ENABLE(ASYNC_SCROLLING) && PLATFORM(MAC)