b7429b839880d198f24abf46479ce9cf00884c6c
[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 "NSScrollerImpDetails.h"
33 #import "PlatformWheelEvent.h"
34 #import "ScrollingCoordinator.h"
35 #import "ScrollingTree.h"
36 #import "ScrollingStateTree.h"
37 #import "Settings.h"
38 #import "TileController.h"
39 #import "WebLayer.h"
40
41 #import <QuartzCore/QuartzCore.h>
42 #import <wtf/CurrentTime.h>
43 #import <wtf/Deque.h>
44 #import <wtf/text/StringBuilder.h>
45 #import <wtf/text/CString.h>
46
47 namespace WebCore {
48
49 static void logThreadedScrollingMode(unsigned synchronousScrollingReasons);
50 static void logWheelEventHandlerCountChanged(unsigned);
51
52
53 PassRefPtr<ScrollingTreeFrameScrollingNode> ScrollingTreeFrameScrollingNodeMac::create(ScrollingTree& scrollingTree, ScrollingNodeID nodeID)
54 {
55     return adoptRef(new ScrollingTreeFrameScrollingNodeMac(scrollingTree, nodeID));
56 }
57
58 ScrollingTreeFrameScrollingNodeMac::ScrollingTreeFrameScrollingNodeMac(ScrollingTree& scrollingTree, ScrollingNodeID nodeID)
59     : ScrollingTreeFrameScrollingNode(scrollingTree, nodeID)
60     , m_scrollController(this)
61     , m_verticalScrollbarPainter(0)
62     , m_horizontalScrollbarPainter(0)
63     , m_lastScrollHadUnfilledPixels(false)
64     , m_hadFirstUpdate(false)
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 (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::WheelEventHandlerCount)) {
137         if (scrollingTree().scrollingPerformanceLoggingEnabled())
138             logWheelEventHandlerCountChanged(scrollingStateNode.wheelEventHandlerCount());
139     }
140
141 #if ENABLE(CSS_SCROLL_SNAP)
142     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::HorizontalSnapOffsets))
143         m_scrollController.updateScrollSnapPoints(ScrollEventAxis::Horizontal, convertToLayoutUnits(scrollingStateNode.horizontalSnapOffsets()));
144
145     if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::VerticalSnapOffsets))
146         m_scrollController.updateScrollSnapPoints(ScrollEventAxis::Vertical, convertToLayoutUnits(scrollingStateNode.verticalSnapOffsets()));
147 #endif
148
149     m_hadFirstUpdate = true;
150 }
151
152 void ScrollingTreeFrameScrollingNodeMac::updateAfterChildren(const ScrollingStateNode& stateNode)
153 {
154     ScrollingTreeFrameScrollingNode::updateAfterChildren(stateNode);
155
156     const auto& scrollingStateNode = downcast<ScrollingStateScrollingNode>(stateNode);
157
158     // Update the scroll position after child nodes have been updated, because they need to have updated their constraints before any scrolling happens.
159     if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::RequestedScrollPosition))
160         setScrollPosition(scrollingStateNode.requestedScrollPosition());
161
162     if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::ScrollLayer)
163         || scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::TotalContentsSize)
164         || scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ScrollableAreaSize))
165         updateMainFramePinState(scrollPosition());
166 }
167
168 void ScrollingTreeFrameScrollingNodeMac::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
169 {
170     if (!canHaveScrollbars())
171         return;
172
173     if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseBegan) {
174         [m_verticalScrollbarPainter setUsePresentationValue:YES];
175         [m_horizontalScrollbarPainter setUsePresentationValue:YES];
176     }
177     if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded || wheelEvent.momentumPhase() == PlatformWheelEventPhaseCancelled) {
178         [m_verticalScrollbarPainter setUsePresentationValue:NO];
179         [m_horizontalScrollbarPainter setUsePresentationValue:NO];
180     }
181
182     m_scrollController.handleWheelEvent(wheelEvent);
183     scrollingTree().setOrClearLatchedNode(wheelEvent, scrollingNodeID());
184     scrollingTree().handleWheelEventPhase(wheelEvent.phase());
185 }
186
187 // FIXME: We should find a way to share some of the code from newGestureIsStarting(), isAlreadyPinnedInDirectionOfGesture(),
188 // allowsVerticalStretching(), and allowsHorizontalStretching() with the implementation in ScrollAnimatorMac.
189 static bool newGestureIsStarting(const PlatformWheelEvent& wheelEvent)
190 {
191     return wheelEvent.phase() == PlatformWheelEventPhaseMayBegin || wheelEvent.phase() == PlatformWheelEventPhaseBegan;
192 }
193
194 bool ScrollingTreeFrameScrollingNodeMac::isAlreadyPinnedInDirectionOfGesture(const PlatformWheelEvent& wheelEvent, ScrollEventAxis axis)
195 {
196     switch (axis) {
197     case ScrollEventAxis::Vertical:
198         return (wheelEvent.deltaY() > 0 && scrollPosition().y() <= minimumScrollPosition().y()) || (wheelEvent.deltaY() < 0 && scrollPosition().y() >= maximumScrollPosition().y());
199     case ScrollEventAxis::Horizontal:
200         return (wheelEvent.deltaX() > 0 && scrollPosition().x() <= minimumScrollPosition().x()) || (wheelEvent.deltaX() < 0 && scrollPosition().x() >= maximumScrollPosition().x());
201     }
202
203     ASSERT_NOT_REACHED();
204     return false;
205 }
206
207 bool ScrollingTreeFrameScrollingNodeMac::allowsHorizontalStretching(const PlatformWheelEvent& wheelEvent)
208 {
209     switch (horizontalScrollElasticity()) {
210     case ScrollElasticityAutomatic: {
211         bool scrollbarsAllowStretching = hasEnabledHorizontalScrollbar() || !hasEnabledVerticalScrollbar();
212         bool eventPreventsStretching = newGestureIsStarting(wheelEvent) && isAlreadyPinnedInDirectionOfGesture(wheelEvent, ScrollEventAxis::Horizontal);
213         return scrollbarsAllowStretching && !eventPreventsStretching;
214     }
215     case ScrollElasticityNone:
216         return false;
217     case ScrollElasticityAllowed:
218         return true;
219     }
220
221     ASSERT_NOT_REACHED();
222     return false;
223 }
224
225 bool ScrollingTreeFrameScrollingNodeMac::allowsVerticalStretching(const PlatformWheelEvent& wheelEvent)
226 {
227     switch (verticalScrollElasticity()) {
228     case ScrollElasticityAutomatic: {
229         bool scrollbarsAllowStretching = hasEnabledVerticalScrollbar() || !hasEnabledHorizontalScrollbar();
230         bool eventPreventsStretching = newGestureIsStarting(wheelEvent) && isAlreadyPinnedInDirectionOfGesture(wheelEvent, ScrollEventAxis::Vertical);
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 IntSize ScrollingTreeFrameScrollingNodeMac::stretchAmount()
244 {
245     IntSize stretch;
246
247     if (scrollPosition().y() < minimumScrollPosition().y())
248         stretch.setHeight(scrollPosition().y() - minimumScrollPosition().y());
249     else if (scrollPosition().y() > maximumScrollPosition().y())
250         stretch.setHeight(scrollPosition().y() - maximumScrollPosition().y());
251
252     if (scrollPosition().x() < minimumScrollPosition().x())
253         stretch.setWidth(scrollPosition().x() - minimumScrollPosition().x());
254     else if (scrollPosition().x() > maximumScrollPosition().x())
255         stretch.setWidth(scrollPosition().x() - maximumScrollPosition().x());
256
257     if (scrollingTree().rootNode() == this) {
258         if (stretch.isZero())
259             scrollingTree().setMainFrameIsRubberBanding(false);
260         else
261             scrollingTree().setMainFrameIsRubberBanding(true);
262     }
263
264     return stretch;
265 }
266
267 bool ScrollingTreeFrameScrollingNodeMac::pinnedInDirection(const FloatSize& delta)
268 {
269     FloatSize limitDelta;
270
271     if (fabsf(delta.height()) >= fabsf(delta.width())) {
272         if (delta.height() < 0) {
273             // We are trying to scroll up. Make sure we are not pinned to the top.
274             limitDelta.setHeight(scrollPosition().y() - minimumScrollPosition().y());
275         } else {
276             // We are trying to scroll down. Make sure we are not pinned to the bottom.
277             limitDelta.setHeight(maximumScrollPosition().y() - scrollPosition().y());
278         }
279     } else if (delta.width()) {
280         if (delta.width() < 0) {
281             // We are trying to scroll left. Make sure we are not pinned to the left.
282             limitDelta.setWidth(scrollPosition().x() - minimumScrollPosition().x());
283         } else {
284             // We are trying to scroll right. Make sure we are not pinned to the right.
285             limitDelta.setWidth(maximumScrollPosition().x() - scrollPosition().x());
286         }
287     }
288
289     if ((delta.width() || delta.height()) && (limitDelta.width() < 1 && limitDelta.height() < 1))
290         return true;
291
292     return false;
293 }
294
295 bool ScrollingTreeFrameScrollingNodeMac::canScrollHorizontally()
296 {
297     return hasEnabledHorizontalScrollbar();
298 }
299
300 bool ScrollingTreeFrameScrollingNodeMac::canScrollVertically()
301 {
302     return hasEnabledVerticalScrollbar();
303 }
304
305 bool ScrollingTreeFrameScrollingNodeMac::shouldRubberBandInDirection(ScrollDirection)
306 {
307     return true;
308 }
309
310 IntPoint ScrollingTreeFrameScrollingNodeMac::absoluteScrollPosition()
311 {
312     return roundedIntPoint(scrollPosition());
313 }
314
315 void ScrollingTreeFrameScrollingNodeMac::immediateScrollBy(const FloatSize& offset)
316 {
317     scrollBy(offset);
318 }
319
320 void ScrollingTreeFrameScrollingNodeMac::immediateScrollByWithoutContentEdgeConstraints(const FloatSize& offset)
321 {
322     scrollByWithoutContentEdgeConstraints(offset);
323 }
324
325 void ScrollingTreeFrameScrollingNodeMac::stopSnapRubberbandTimer()
326 {
327     scrollingTree().setMainFrameIsRubberBanding(false);
328
329     // Since the rubberband timer has stopped, totalContentsSizeForRubberBand can be synchronized with totalContentsSize.
330     setTotalContentsSizeForRubberBand(totalContentsSize());
331 }
332
333 void ScrollingTreeFrameScrollingNodeMac::adjustScrollPositionToBoundsIfNecessary()
334 {
335     FloatPoint currentScrollPosition = absoluteScrollPosition();
336     FloatPoint minPosition = minimumScrollPosition();
337     FloatPoint maxPosition = maximumScrollPosition();
338
339     float nearestXWithinBounds = std::max(std::min(currentScrollPosition.x(), maxPosition.x()), minPosition.x());
340     float nearestYWithinBounds = std::max(std::min(currentScrollPosition.y(), maxPosition.y()), minPosition.y());
341
342     FloatPoint nearestPointWithinBounds(nearestXWithinBounds, nearestYWithinBounds);
343     immediateScrollBy(nearestPointWithinBounds - currentScrollPosition);
344 }
345
346 FloatPoint ScrollingTreeFrameScrollingNodeMac::scrollPosition() const
347 {
348     if (shouldUpdateScrollLayerPositionSynchronously())
349         return m_probableMainThreadScrollPosition;
350
351     CGPoint scrollLayerPosition = m_scrollLayer.get().position;
352     return FloatPoint(-scrollLayerPosition.x + scrollOrigin().x(), -scrollLayerPosition.y + scrollOrigin().y());
353 }
354
355 void ScrollingTreeFrameScrollingNodeMac::setScrollPosition(const FloatPoint& scrollPosition)
356 {
357     // Scroll deltas can be non-integral with some input devices, so scrollPosition may not be integral.
358     // FIXME: when we support half-pixel scroll positions on Retina displays, this will need to round to half pixels.
359     FloatPoint roundedPosition(roundf(scrollPosition.x()), roundf(scrollPosition.y()));
360
361     ScrollingTreeFrameScrollingNode::setScrollPosition(roundedPosition);
362
363     if (scrollingTree().scrollingPerformanceLoggingEnabled())
364         logExposedUnfilledArea();
365 }
366
367 void ScrollingTreeFrameScrollingNodeMac::setScrollPositionWithoutContentEdgeConstraints(const FloatPoint& scrollPosition)
368 {
369     updateMainFramePinState(scrollPosition);
370
371     if (shouldUpdateScrollLayerPositionSynchronously()) {
372         m_probableMainThreadScrollPosition = scrollPosition;
373         scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition, SetScrollingLayerPosition);
374         return;
375     }
376
377     setScrollLayerPosition(scrollPosition);
378     scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition);
379 }
380
381 void ScrollingTreeFrameScrollingNodeMac::setScrollLayerPosition(const FloatPoint& position)
382 {
383     ASSERT(!shouldUpdateScrollLayerPositionSynchronously());
384     m_scrollLayer.get().position = CGPointMake(-position.x() + scrollOrigin().x(), -position.y() + scrollOrigin().y());
385
386     ScrollBehaviorForFixedElements behaviorForFixed = scrollBehaviorForFixedElements();
387     LayoutPoint scrollOffset = LayoutPoint(position) - toLayoutSize(scrollOrigin());
388     FloatRect viewportRect(FloatPoint(), scrollableAreaSize());
389     
390     FloatSize scrollOffsetForFixedChildren = FrameView::scrollOffsetForFixedPosition(enclosingLayoutRect(viewportRect), LayoutSize(totalContentsSize()), scrollOffset, scrollOrigin(), frameScaleFactor(),
391         false, behaviorForFixed, headerHeight(), footerHeight());
392     
393     if (m_counterScrollingLayer)
394         m_counterScrollingLayer.get().position = FloatPoint(scrollOffsetForFixedChildren);
395
396     float topContentInset = this->topContentInset();
397     if (m_insetClipLayer && m_scrolledContentsLayer && topContentInset) {
398         m_insetClipLayer.get().position = FloatPoint(0, FrameView::yPositionForInsetClipLayer(position, topContentInset));
399         m_scrolledContentsLayer.get().position = FloatPoint(m_scrolledContentsLayer.get().position.x,
400             FrameView::yPositionForRootContentLayer(position, topContentInset, headerHeight()));
401         if (m_contentShadowLayer)
402             m_contentShadowLayer.get().position = m_scrolledContentsLayer.get().position;
403     }
404
405     if (m_headerLayer || m_footerLayer) {
406         // Generally the banners should have the same horizontal-position computation as a fixed element. However,
407         // the banners are not affected by the frameScaleFactor(), so if there is currently a non-1 frameScaleFactor()
408         // then we should recompute scrollOffsetForFixedChildren for the banner with a scale factor of 1.
409         float horizontalScrollOffsetForBanner = scrollOffsetForFixedChildren.width();
410         if (frameScaleFactor() != 1)
411             horizontalScrollOffsetForBanner = FrameView::scrollOffsetForFixedPosition(enclosingLayoutRect(viewportRect), LayoutSize(totalContentsSize()), scrollOffset, scrollOrigin(), 1, false, behaviorForFixed, headerHeight(), footerHeight()).width();
412
413         if (m_headerLayer)
414             m_headerLayer.get().position = FloatPoint(horizontalScrollOffsetForBanner, FrameView::yPositionForHeaderLayer(position, topContentInset));
415
416         if (m_footerLayer) {
417             m_footerLayer.get().position = FloatPoint(horizontalScrollOffsetForBanner,
418                 FrameView::yPositionForFooterLayer(position, topContentInset, totalContentsSize().height(), footerHeight()));
419         }
420     }
421
422     if (m_verticalScrollbarPainter || m_horizontalScrollbarPainter) {
423         [CATransaction begin];
424         [CATransaction lock];
425
426         if ([m_verticalScrollbarPainter shouldUsePresentationValue]) {
427             float presentationValue;
428             float overhangAmount;
429             ScrollableArea::computeScrollbarValueAndOverhang(position.y(), totalContentsSize().height(), viewportRect.height(), presentationValue, overhangAmount);
430             [m_verticalScrollbarPainter setPresentationValue:presentationValue];
431         }
432
433         if ([m_horizontalScrollbarPainter shouldUsePresentationValue]) {
434             float presentationValue;
435             float overhangAmount;
436             ScrollableArea::computeScrollbarValueAndOverhang(position.x(), totalContentsSize().width(), viewportRect.width(), presentationValue, overhangAmount);
437             [m_horizontalScrollbarPainter setPresentationValue:presentationValue];
438         }
439         [CATransaction unlock];
440         [CATransaction commit];
441     }
442
443     if (!m_children)
444         return;
445
446     viewportRect.setLocation(FloatPoint() + scrollOffsetForFixedChildren);
447
448     for (auto& child : *m_children)
449         child->updateLayersAfterAncestorChange(*this, viewportRect, FloatSize());
450 }
451
452 void ScrollingTreeFrameScrollingNodeMac::updateLayersAfterViewportChange(const FloatRect&, double)
453 {
454     ASSERT_NOT_REACHED();
455 }
456
457 FloatPoint ScrollingTreeFrameScrollingNodeMac::minimumScrollPosition() const
458 {
459     FloatPoint position;
460     
461     if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToBottom)
462         position.setY(maximumScrollPosition().y());
463
464     return position;
465 }
466
467 FloatPoint ScrollingTreeFrameScrollingNodeMac::maximumScrollPosition() const
468 {
469     FloatPoint position(totalContentsSizeForRubberBand() - scrollableAreaSize());
470     position = position.expandedTo(FloatPoint());
471
472     if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToTop)
473         position.setY(minimumScrollPosition().y());
474
475     return position;
476 }
477
478 void ScrollingTreeFrameScrollingNodeMac::updateMainFramePinState(const FloatPoint& scrollPosition)
479 {
480     bool pinnedToTheLeft = scrollPosition.x() <= minimumScrollPosition().x();
481     bool pinnedToTheRight = scrollPosition.x() >= maximumScrollPosition().x();
482     bool pinnedToTheTop = scrollPosition.y() <= minimumScrollPosition().y();
483     bool pinnedToTheBottom = scrollPosition.y() >= maximumScrollPosition().y();
484
485     scrollingTree().setMainFramePinState(pinnedToTheLeft, pinnedToTheRight, pinnedToTheTop, pinnedToTheBottom);
486 }
487
488 void ScrollingTreeFrameScrollingNodeMac::logExposedUnfilledArea()
489 {
490     Region paintedVisibleTiles;
491
492     Deque<CALayer*> layerQueue;
493     layerQueue.append(m_scrollLayer.get());
494     PlatformLayerList tiles;
495
496     while (!layerQueue.isEmpty() && tiles.isEmpty()) {
497         CALayer* layer = layerQueue.takeFirst();
498         NSArray* sublayers = [[layer sublayers] copy];
499
500         // If this layer is the parent of a tile, it is the parent of all of the tiles and nothing else.
501         if ([[[sublayers objectAtIndex:0] valueForKey:@"isTile"] boolValue]) {
502             for (CALayer* sublayer in sublayers)
503                 tiles.append(sublayer);
504         } else {
505             for (CALayer* sublayer in sublayers)
506                 layerQueue.append(sublayer);
507         }
508
509         [sublayers release];
510     }
511
512     FloatPoint scrollPosition = this->scrollPosition();
513     FloatRect viewPortRect(FloatPoint(), scrollableAreaSize());
514     unsigned unfilledArea = TileController::blankPixelCountForTiles(tiles, viewPortRect, IntPoint(-scrollPosition.x(), -scrollPosition.y()));
515
516     if (unfilledArea || m_lastScrollHadUnfilledPixels)
517         WTFLogAlways("SCROLLING: Exposed tileless area. Time: %f Unfilled Pixels: %u\n", WTF::monotonicallyIncreasingTime(), unfilledArea);
518
519     m_lastScrollHadUnfilledPixels = unfilledArea;
520 }
521
522 static void logThreadedScrollingMode(unsigned synchronousScrollingReasons)
523 {
524     if (synchronousScrollingReasons) {
525         StringBuilder reasonsDescription;
526
527         if (synchronousScrollingReasons & ScrollingCoordinator::ForcedOnMainThread)
528             reasonsDescription.appendLiteral("forced,");
529         if (synchronousScrollingReasons & ScrollingCoordinator::HasSlowRepaintObjects)
530             reasonsDescription.appendLiteral("slow-repaint objects,");
531         if (synchronousScrollingReasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers)
532             reasonsDescription.appendLiteral("viewport-constrained objects,");
533         if (synchronousScrollingReasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects)
534             reasonsDescription.appendLiteral("non-layer viewport-constrained objects,");
535         if (synchronousScrollingReasons & ScrollingCoordinator::IsImageDocument)
536             reasonsDescription.appendLiteral("image document,");
537
538         // Strip the trailing comma.
539         String reasonsDescriptionTrimmed = reasonsDescription.toString().left(reasonsDescription.length() - 1);
540
541         WTFLogAlways("SCROLLING: Switching to main-thread scrolling mode. Time: %f Reason(s): %s\n", WTF::monotonicallyIncreasingTime(), reasonsDescriptionTrimmed.ascii().data());
542     } else
543         WTFLogAlways("SCROLLING: Switching to threaded scrolling mode. Time: %f\n", WTF::monotonicallyIncreasingTime());
544 }
545
546 void logWheelEventHandlerCountChanged(unsigned count)
547 {
548     WTFLogAlways("SCROLLING: Wheel event handler count changed. Time: %f Count: %u\n", WTF::monotonicallyIncreasingTime(), count);
549 }
550
551 #if ENABLE(CSS_SCROLL_SNAP) && PLATFORM(MAC)
552 LayoutUnit ScrollingTreeFrameScrollingNodeMac::scrollOffsetOnAxis(ScrollEventAxis axis) const
553 {
554     const FloatPoint& currentPosition = scrollPosition();
555     return axis == ScrollEventAxis::Horizontal ? currentPosition.x() : currentPosition.y();
556 }
557
558 void ScrollingTreeFrameScrollingNodeMac::immediateScrollOnAxis(ScrollEventAxis axis, float delta)
559 {
560     const FloatPoint& currentPosition = scrollPosition();
561     FloatPoint change;
562     if (axis == ScrollEventAxis::Horizontal)
563         change = FloatPoint(currentPosition.x() + delta, currentPosition.y());
564     else
565         change = FloatPoint(currentPosition.x(), currentPosition.y() + delta);
566
567     immediateScrollBy(change - currentPosition);
568 }
569
570 float ScrollingTreeFrameScrollingNodeMac::pageScaleFactor() const
571 {
572     return frameScaleFactor();
573 }
574 #endif
575
576 } // namespace WebCore
577
578 #endif // ENABLE(ASYNC_SCROLLING) && PLATFORM(MAC)