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