blob: 243196cc928d8fb28de514269b70261bbe5c4af7 [file] [log] [blame]
/*
* Copyright (C) 2012, 2014-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "ScrollingTreeFrameScrollingNodeMac.h"
#if ENABLE(ASYNC_SCROLLING) && PLATFORM(MAC)
#import "FrameView.h"
#import "LayoutSize.h"
#import "Logging.h"
#import "PlatformWheelEvent.h"
#import "ScrollableArea.h"
#import "ScrollingCoordinator.h"
#import "ScrollingStateTree.h"
#import "ScrollingTree.h"
#import "TileController.h"
#import "WebLayer.h"
#import <pal/spi/mac/NSScrollerImpSPI.h>
#import <wtf/text/TextStream.h>
#import <QuartzCore/QuartzCore.h>
#import <wtf/Deque.h>
#import <wtf/text/CString.h>
namespace WebCore {
Ref<ScrollingTreeFrameScrollingNode> ScrollingTreeFrameScrollingNodeMac::create(ScrollingTree& scrollingTree, ScrollingNodeType nodeType, ScrollingNodeID nodeID)
{
return adoptRef(*new ScrollingTreeFrameScrollingNodeMac(scrollingTree, nodeType, nodeID));
}
ScrollingTreeFrameScrollingNodeMac::ScrollingTreeFrameScrollingNodeMac(ScrollingTree& scrollingTree, ScrollingNodeType nodeType, ScrollingNodeID nodeID)
: ScrollingTreeFrameScrollingNode(scrollingTree, nodeType, nodeID)
, m_scrollController(*this)
, m_verticalScrollerImp(nullptr)
, m_horizontalScrollerImp(nullptr)
{
}
ScrollingTreeFrameScrollingNodeMac::~ScrollingTreeFrameScrollingNodeMac()
{
releaseReferencesToScrollerImpsOnTheMainThread();
}
void ScrollingTreeFrameScrollingNodeMac::releaseReferencesToScrollerImpsOnTheMainThread()
{
if (m_verticalScrollerImp || m_horizontalScrollerImp) {
// FIXME: This is a workaround in place for the time being since NSScrollerImps cannot be deallocated
// on a non-main thread. rdar://problem/24535055
WTF::callOnMainThread([verticalScrollerImp = WTFMove(m_verticalScrollerImp), horizontalScrollerImp = WTFMove(m_horizontalScrollerImp)] {
});
}
}
#if ENABLE(CSS_SCROLL_SNAP)
static inline Vector<LayoutUnit> convertToLayoutUnits(const Vector<float>& snapOffsetsAsFloat)
{
Vector<LayoutUnit> snapOffsets;
snapOffsets.reserveInitialCapacity(snapOffsetsAsFloat.size());
for (auto offset : snapOffsetsAsFloat)
snapOffsets.uncheckedAppend(offset);
return snapOffsets;
}
static inline Vector<ScrollOffsetRange<LayoutUnit>> convertToLayoutUnits(const Vector<ScrollOffsetRange<float>>& snapOffsetRangesAsFloat)
{
Vector<ScrollOffsetRange<LayoutUnit>> snapOffsetRanges;
snapOffsetRanges.reserveInitialCapacity(snapOffsetRangesAsFloat.size());
for (auto range : snapOffsetRangesAsFloat)
snapOffsetRanges.uncheckedAppend({ range.start, range.end });
return snapOffsetRanges;
}
#endif
void ScrollingTreeFrameScrollingNodeMac::commitStateBeforeChildren(const ScrollingStateNode& stateNode)
{
ScrollingTreeFrameScrollingNode::commitStateBeforeChildren(stateNode);
const auto& scrollingStateNode = downcast<ScrollingStateFrameScrollingNode>(stateNode);
if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::ScrollLayer))
m_scrollLayer = scrollingStateNode.layer();
if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ScrolledContentsLayer))
m_scrolledContentsLayer = scrollingStateNode.scrolledContentsLayer();
if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::CounterScrollingLayer))
m_counterScrollingLayer = scrollingStateNode.counterScrollingLayer();
if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::InsetClipLayer))
m_insetClipLayer = scrollingStateNode.insetClipLayer();
if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::ContentShadowLayer))
m_contentShadowLayer = scrollingStateNode.contentShadowLayer();
if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::HeaderLayer))
m_headerLayer = scrollingStateNode.headerLayer();
if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::FooterLayer))
m_footerLayer = scrollingStateNode.footerLayer();
if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::PainterForScrollbar)) {
releaseReferencesToScrollerImpsOnTheMainThread();
m_verticalScrollerImp = scrollingStateNode.verticalScrollerImp();
m_horizontalScrollerImp = scrollingStateNode.horizontalScrollerImp();
}
bool logScrollingMode = !m_hadFirstUpdate;
if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::ReasonsForSynchronousScrolling)) {
if (shouldUpdateScrollLayerPositionSynchronously()) {
// We're transitioning to the slow "update scroll layer position on the main thread" mode.
// Initialize the probable main thread scroll position with the current scroll layer position.
if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::RequestedScrollPosition))
m_probableMainThreadScrollPosition = scrollingStateNode.requestedScrollPosition();
else {
CGPoint scrollLayerPosition = m_scrollLayer.get().position;
m_probableMainThreadScrollPosition = FloatPoint(-scrollLayerPosition.x, -scrollLayerPosition.y);
}
}
logScrollingMode = true;
}
if (logScrollingMode && scrollingTree().scrollingPerformanceLoggingEnabled())
scrollingTree().reportSynchronousScrollingReasonsChanged(MonotonicTime::now(), synchronousScrollingReasons());
#if ENABLE(CSS_SCROLL_SNAP)
if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::HorizontalSnapOffsets) || scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::HorizontalSnapOffsetRanges))
m_scrollController.updateScrollSnapPoints(ScrollEventAxis::Horizontal, convertToLayoutUnits(scrollingStateNode.horizontalSnapOffsets()), convertToLayoutUnits(scrollingStateNode.horizontalSnapOffsetRanges()));
if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::VerticalSnapOffsets) || scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::VerticalSnapOffsetRanges))
m_scrollController.updateScrollSnapPoints(ScrollEventAxis::Vertical, convertToLayoutUnits(scrollingStateNode.verticalSnapOffsets()), convertToLayoutUnits(scrollingStateNode.verticalSnapOffsetRanges()));
if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::CurrentHorizontalSnapOffsetIndex))
m_scrollController.setActiveScrollSnapIndexForAxis(ScrollEventAxis::Horizontal, scrollingStateNode.currentHorizontalSnapPointIndex());
if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::CurrentVerticalSnapOffsetIndex))
m_scrollController.setActiveScrollSnapIndexForAxis(ScrollEventAxis::Vertical, scrollingStateNode.currentVerticalSnapPointIndex());
#endif
if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ExpectsWheelEventTestTrigger))
m_expectsWheelEventTestTrigger = scrollingStateNode.expectsWheelEventTestTrigger();
m_hadFirstUpdate = true;
}
void ScrollingTreeFrameScrollingNodeMac::commitStateAfterChildren(const ScrollingStateNode& stateNode)
{
ScrollingTreeFrameScrollingNode::commitStateAfterChildren(stateNode);
const auto& scrollingStateNode = downcast<ScrollingStateScrollingNode>(stateNode);
// Update the scroll position after child nodes have been updated, because they need to have updated their constraints before any scrolling happens.
if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::RequestedScrollPosition))
setScrollPosition(scrollingStateNode.requestedScrollPosition());
if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::ScrollLayer)
|| scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::TotalContentsSize)
|| scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ScrollableAreaSize))
updateMainFramePinState(scrollPosition());
}
void ScrollingTreeFrameScrollingNodeMac::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
{
if (!canHaveScrollbars())
return;
if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseBegan) {
[m_verticalScrollerImp setUsePresentationValue:YES];
[m_horizontalScrollerImp setUsePresentationValue:YES];
}
if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded || wheelEvent.momentumPhase() == PlatformWheelEventPhaseCancelled) {
[m_verticalScrollerImp setUsePresentationValue:NO];
[m_horizontalScrollerImp setUsePresentationValue:NO];
}
#if ENABLE(CSS_SCROLL_SNAP) || ENABLE(RUBBER_BANDING)
if (m_expectsWheelEventTestTrigger) {
if (scrollingTree().shouldHandleWheelEventSynchronously(wheelEvent))
removeTestDeferralForReason(reinterpret_cast<WheelEventTestTrigger::ScrollableAreaIdentifier>(scrollingNodeID()), WheelEventTestTrigger::ScrollingThreadSyncNeeded);
else
deferTestsForReason(reinterpret_cast<WheelEventTestTrigger::ScrollableAreaIdentifier>(scrollingNodeID()), WheelEventTestTrigger::ScrollingThreadSyncNeeded);
}
#endif
m_scrollController.handleWheelEvent(wheelEvent);
#if ENABLE(CSS_SCROLL_SNAP)
scrollingTree().setMainFrameIsScrollSnapping(m_scrollController.isScrollSnapInProgress());
if (m_scrollController.activeScrollSnapIndexDidChange())
scrollingTree().setActiveScrollSnapIndices(scrollingNodeID(), m_scrollController.activeScrollSnapIndexForAxis(ScrollEventAxis::Horizontal), m_scrollController.activeScrollSnapIndexForAxis(ScrollEventAxis::Vertical));
#endif
scrollingTree().setOrClearLatchedNode(wheelEvent, scrollingNodeID());
scrollingTree().handleWheelEventPhase(wheelEvent.phase());
}
// FIXME: We should find a way to share some of the code from newGestureIsStarting(), isAlreadyPinnedInDirectionOfGesture(),
// allowsVerticalStretching(), and allowsHorizontalStretching() with the implementation in ScrollAnimatorMac.
static bool newGestureIsStarting(const PlatformWheelEvent& wheelEvent)
{
return wheelEvent.phase() == PlatformWheelEventPhaseMayBegin || wheelEvent.phase() == PlatformWheelEventPhaseBegan;
}
bool ScrollingTreeFrameScrollingNodeMac::isAlreadyPinnedInDirectionOfGesture(const PlatformWheelEvent& wheelEvent, ScrollEventAxis axis)
{
switch (axis) {
case ScrollEventAxis::Vertical:
return (wheelEvent.deltaY() > 0 && scrollPosition().y() <= minimumScrollPosition().y()) || (wheelEvent.deltaY() < 0 && scrollPosition().y() >= maximumScrollPosition().y());
case ScrollEventAxis::Horizontal:
return (wheelEvent.deltaX() > 0 && scrollPosition().x() <= minimumScrollPosition().x()) || (wheelEvent.deltaX() < 0 && scrollPosition().x() >= maximumScrollPosition().x());
}
ASSERT_NOT_REACHED();
return false;
}
bool ScrollingTreeFrameScrollingNodeMac::allowsHorizontalStretching(const PlatformWheelEvent& wheelEvent)
{
switch (horizontalScrollElasticity()) {
case ScrollElasticityAutomatic: {
bool scrollbarsAllowStretching = hasEnabledHorizontalScrollbar() || !hasEnabledVerticalScrollbar();
bool eventPreventsStretching = newGestureIsStarting(wheelEvent) && isAlreadyPinnedInDirectionOfGesture(wheelEvent, ScrollEventAxis::Horizontal);
return scrollbarsAllowStretching && !eventPreventsStretching;
}
case ScrollElasticityNone:
return false;
case ScrollElasticityAllowed:
return true;
}
ASSERT_NOT_REACHED();
return false;
}
bool ScrollingTreeFrameScrollingNodeMac::allowsVerticalStretching(const PlatformWheelEvent& wheelEvent)
{
switch (verticalScrollElasticity()) {
case ScrollElasticityAutomatic: {
bool scrollbarsAllowStretching = hasEnabledVerticalScrollbar() || !hasEnabledHorizontalScrollbar();
bool eventPreventsStretching = newGestureIsStarting(wheelEvent) && isAlreadyPinnedInDirectionOfGesture(wheelEvent, ScrollEventAxis::Vertical);
return scrollbarsAllowStretching && !eventPreventsStretching;
}
case ScrollElasticityNone:
return false;
case ScrollElasticityAllowed:
return true;
}
ASSERT_NOT_REACHED();
return false;
}
IntSize ScrollingTreeFrameScrollingNodeMac::stretchAmount()
{
IntSize stretch;
if (scrollPosition().y() < minimumScrollPosition().y())
stretch.setHeight(scrollPosition().y() - minimumScrollPosition().y());
else if (scrollPosition().y() > maximumScrollPosition().y())
stretch.setHeight(scrollPosition().y() - maximumScrollPosition().y());
if (scrollPosition().x() < minimumScrollPosition().x())
stretch.setWidth(scrollPosition().x() - minimumScrollPosition().x());
else if (scrollPosition().x() > maximumScrollPosition().x())
stretch.setWidth(scrollPosition().x() - maximumScrollPosition().x());
if (scrollingTree().rootNode() == this) {
if (stretch.isZero())
scrollingTree().setMainFrameIsRubberBanding(false);
else
scrollingTree().setMainFrameIsRubberBanding(true);
}
return stretch;
}
bool ScrollingTreeFrameScrollingNodeMac::pinnedInDirection(const FloatSize& delta)
{
FloatSize limitDelta;
if (fabsf(delta.height()) >= fabsf(delta.width())) {
if (delta.height() < 0) {
// We are trying to scroll up. Make sure we are not pinned to the top.
limitDelta.setHeight(scrollPosition().y() - minimumScrollPosition().y());
} else {
// We are trying to scroll down. Make sure we are not pinned to the bottom.
limitDelta.setHeight(maximumScrollPosition().y() - scrollPosition().y());
}
} else if (delta.width()) {
if (delta.width() < 0) {
// We are trying to scroll left. Make sure we are not pinned to the left.
limitDelta.setWidth(scrollPosition().x() - minimumScrollPosition().x());
} else {
// We are trying to scroll right. Make sure we are not pinned to the right.
limitDelta.setWidth(maximumScrollPosition().x() - scrollPosition().x());
}
}
if ((delta.width() || delta.height()) && (limitDelta.width() < 1 && limitDelta.height() < 1))
return true;
return false;
}
bool ScrollingTreeFrameScrollingNodeMac::canScrollHorizontally()
{
return hasEnabledHorizontalScrollbar();
}
bool ScrollingTreeFrameScrollingNodeMac::canScrollVertically()
{
return hasEnabledVerticalScrollbar();
}
bool ScrollingTreeFrameScrollingNodeMac::shouldRubberBandInDirection(ScrollDirection)
{
return true;
}
void ScrollingTreeFrameScrollingNodeMac::immediateScrollBy(const FloatSize& delta)
{
scrollBy(delta);
}
void ScrollingTreeFrameScrollingNodeMac::immediateScrollByWithoutContentEdgeConstraints(const FloatSize& offset)
{
scrollByWithoutContentEdgeConstraints(offset);
}
void ScrollingTreeFrameScrollingNodeMac::stopSnapRubberbandTimer()
{
scrollingTree().setMainFrameIsRubberBanding(false);
// Since the rubberband timer has stopped, totalContentsSizeForRubberBand can be synchronized with totalContentsSize.
setTotalContentsSizeForRubberBand(totalContentsSize());
}
void ScrollingTreeFrameScrollingNodeMac::adjustScrollPositionToBoundsIfNecessary()
{
FloatPoint currentScrollPosition = scrollPosition();
FloatPoint constainedPosition = currentScrollPosition.constrainedBetween(minimumScrollPosition(), maximumScrollPosition());
immediateScrollBy(constainedPosition - currentScrollPosition);
}
FloatPoint ScrollingTreeFrameScrollingNodeMac::scrollPosition() const
{
if (shouldUpdateScrollLayerPositionSynchronously())
return m_probableMainThreadScrollPosition;
return -m_scrollLayer.get().position;
}
void ScrollingTreeFrameScrollingNodeMac::setScrollPosition(const FloatPoint& scrollPosition)
{
LOG_WITH_STREAM(Scrolling, stream << "ScrollingTreeFrameScrollingNodeMac::setScrollPosition " << scrollPosition << " scrollPosition(): " << this->scrollPosition() << " min: " << minimumScrollPosition() << " max: " << maximumScrollPosition());
// Scroll deltas can be non-integral with some input devices, so scrollPosition may not be integral.
// FIXME: when we support half-pixel scroll positions on Retina displays, this will need to round to half pixels.
FloatPoint roundedPosition(roundf(scrollPosition.x()), roundf(scrollPosition.y()));
ScrollingTreeFrameScrollingNode::setScrollPosition(roundedPosition);
if (scrollingTree().scrollingPerformanceLoggingEnabled()) {
unsigned unfilledArea = exposedUnfilledArea();
if (unfilledArea || m_lastScrollHadUnfilledPixels)
scrollingTree().reportExposedUnfilledArea(MonotonicTime::now(), unfilledArea);
m_lastScrollHadUnfilledPixels = unfilledArea;
}
}
void ScrollingTreeFrameScrollingNodeMac::setScrollPositionWithoutContentEdgeConstraints(const FloatPoint& scrollPosition)
{
updateMainFramePinState(scrollPosition);
std::optional<FloatPoint> layoutViewportOrigin;
if (scrollingTree().visualViewportEnabled()) {
FloatPoint visibleContentOrigin = scrollPosition;
FloatRect newLayoutViewport = layoutViewportForScrollPosition(visibleContentOrigin, frameScaleFactor());
setLayoutViewport(newLayoutViewport);
layoutViewportOrigin = newLayoutViewport.location();
}
if (shouldUpdateScrollLayerPositionSynchronously()) {
m_probableMainThreadScrollPosition = scrollPosition;
scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition, layoutViewportOrigin, ScrollingLayerPositionAction::Set);
return;
}
setScrollLayerPosition(scrollPosition, layoutViewport());
scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition, layoutViewportOrigin);
}
void ScrollingTreeFrameScrollingNodeMac::setScrollLayerPosition(const FloatPoint& position, const FloatRect& layoutViewport)
{
ASSERT(!shouldUpdateScrollLayerPositionSynchronously());
m_scrollLayer.get().position = -position;
FloatRect visibleContentRect(position, scrollableAreaSize());
FloatRect fixedPositionRect;
ScrollBehaviorForFixedElements behaviorForFixed = StickToViewportBounds;
if (scrollingTree().visualViewportEnabled())
fixedPositionRect = layoutViewport;
else {
behaviorForFixed = scrollBehaviorForFixedElements();
FloatPoint scrollPositionForFixedChildren = FrameView::scrollPositionForFixedPosition(enclosingLayoutRect(visibleContentRect), LayoutSize(totalContentsSize()),
LayoutPoint(position), scrollOrigin(), frameScaleFactor(), fixedElementsLayoutRelativeToFrame(), behaviorForFixed, headerHeight(), footerHeight());
fixedPositionRect = { scrollPositionForFixedChildren, visibleContentRect.size() };
}
if (m_counterScrollingLayer)
m_counterScrollingLayer.get().position = fixedPositionRect.location();
float topContentInset = this->topContentInset();
if (m_insetClipLayer && m_scrolledContentsLayer && topContentInset) {
m_insetClipLayer.get().position = FloatPoint(m_insetClipLayer.get().position.x, FrameView::yPositionForInsetClipLayer(position, topContentInset));
m_scrolledContentsLayer.get().position = FrameView::positionForRootContentLayer(position, scrollOrigin(), topContentInset, headerHeight());
if (m_contentShadowLayer)
m_contentShadowLayer.get().position = m_scrolledContentsLayer.get().position;
}
if (m_headerLayer || m_footerLayer) {
// Generally the banners should have the same horizontal-position computation as a fixed element. However,
// the banners are not affected by the frameScaleFactor(), so if there is currently a non-1 frameScaleFactor()
// then we should recompute fixedPositionRect.x() for the banner with a scale factor of 1.
float horizontalScrollOffsetForBanner = fixedPositionRect.x();
if (!scrollingTree().visualViewportEnabled() && frameScaleFactor() != 1) {
horizontalScrollOffsetForBanner = FrameView::scrollPositionForFixedPosition(enclosingLayoutRect(visibleContentRect), LayoutSize(totalContentsSize()),
LayoutPoint(position), scrollOrigin(), 1, fixedElementsLayoutRelativeToFrame(), behaviorForFixed, headerHeight(), footerHeight()).x();
}
if (m_headerLayer)
m_headerLayer.get().position = FloatPoint(horizontalScrollOffsetForBanner, FrameView::yPositionForHeaderLayer(position, topContentInset));
if (m_footerLayer)
m_footerLayer.get().position = FloatPoint(horizontalScrollOffsetForBanner, FrameView::yPositionForFooterLayer(position, topContentInset, totalContentsSize().height(), footerHeight()));
}
if (m_verticalScrollerImp || m_horizontalScrollerImp) {
[CATransaction begin];
[CATransaction lock];
if ([m_verticalScrollerImp shouldUsePresentationValue]) {
float presentationValue;
float overhangAmount;
ScrollableArea::computeScrollbarValueAndOverhang(position.y(), totalContentsSize().height(), visibleContentRect.height(), presentationValue, overhangAmount);
[m_verticalScrollerImp setPresentationValue:presentationValue];
}
if ([m_horizontalScrollerImp shouldUsePresentationValue]) {
float presentationValue;
float overhangAmount;
ScrollableArea::computeScrollbarValueAndOverhang(position.x(), totalContentsSize().width(), visibleContentRect.width(), presentationValue, overhangAmount);
[m_horizontalScrollerImp setPresentationValue:presentationValue];
}
[CATransaction unlock];
[CATransaction commit];
}
if (!m_children)
return;
for (auto& child : *m_children)
child->updateLayersAfterAncestorChange(*this, fixedPositionRect, FloatSize());
}
void ScrollingTreeFrameScrollingNodeMac::updateLayersAfterViewportChange(const FloatRect&, double)
{
ASSERT_NOT_REACHED();
}
FloatPoint ScrollingTreeFrameScrollingNodeMac::minimumScrollPosition() const
{
FloatPoint position = ScrollableArea::scrollPositionFromOffset(FloatPoint(), toFloatSize(scrollOrigin()));
if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToBottom)
position.setY(maximumScrollPosition().y());
return position;
}
FloatPoint ScrollingTreeFrameScrollingNodeMac::maximumScrollPosition() const
{
FloatPoint position = ScrollableArea::scrollPositionFromOffset(FloatPoint(totalContentsSizeForRubberBand() - scrollableAreaSize()), toFloatSize(scrollOrigin()));
position = position.expandedTo(FloatPoint());
if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToTop)
position.setY(minimumScrollPosition().y());
return position;
}
void ScrollingTreeFrameScrollingNodeMac::updateMainFramePinState(const FloatPoint& scrollPosition)
{
bool pinnedToTheLeft = scrollPosition.x() <= minimumScrollPosition().x();
bool pinnedToTheRight = scrollPosition.x() >= maximumScrollPosition().x();
bool pinnedToTheTop = scrollPosition.y() <= minimumScrollPosition().y();
bool pinnedToTheBottom = scrollPosition.y() >= maximumScrollPosition().y();
scrollingTree().setMainFramePinState(pinnedToTheLeft, pinnedToTheRight, pinnedToTheTop, pinnedToTheBottom);
}
unsigned ScrollingTreeFrameScrollingNodeMac::exposedUnfilledArea() const
{
Region paintedVisibleTiles;
Deque<CALayer*> layerQueue;
layerQueue.append(m_scrollLayer.get());
PlatformLayerList tiles;
while (!layerQueue.isEmpty() && tiles.isEmpty()) {
CALayer* layer = layerQueue.takeFirst();
NSArray* sublayers = [[layer sublayers] copy];
// If this layer is the parent of a tile, it is the parent of all of the tiles and nothing else.
if ([[[sublayers objectAtIndex:0] valueForKey:@"isTile"] boolValue]) {
for (CALayer* sublayer in sublayers)
tiles.append(sublayer);
} else {
for (CALayer* sublayer in sublayers)
layerQueue.append(sublayer);
}
[sublayers release];
}
FloatPoint scrollPosition = this->scrollPosition();
FloatRect viewPortRect(FloatPoint(), scrollableAreaSize());
return TileController::blankPixelCountForTiles(tiles, viewPortRect, IntPoint(-scrollPosition.x(), -scrollPosition.y()));
}
#if ENABLE(CSS_SCROLL_SNAP)
FloatPoint ScrollingTreeFrameScrollingNodeMac::scrollOffset() const
{
return scrollPosition();
}
void ScrollingTreeFrameScrollingNodeMac::immediateScrollOnAxis(ScrollEventAxis axis, float delta)
{
const FloatPoint& currentPosition = scrollPosition();
FloatPoint change;
if (axis == ScrollEventAxis::Horizontal)
change = FloatPoint(currentPosition.x() + delta, currentPosition.y());
else
change = FloatPoint(currentPosition.x(), currentPosition.y() + delta);
immediateScrollBy(change - currentPosition);
}
float ScrollingTreeFrameScrollingNodeMac::pageScaleFactor() const
{
return frameScaleFactor();
}
void ScrollingTreeFrameScrollingNodeMac::startScrollSnapTimer()
{
scrollingTree().setMainFrameIsScrollSnapping(true);
}
void ScrollingTreeFrameScrollingNodeMac::stopScrollSnapTimer()
{
scrollingTree().setMainFrameIsScrollSnapping(false);
}
LayoutSize ScrollingTreeFrameScrollingNodeMac::scrollExtent() const
{
return LayoutSize(totalContentsSize());
}
FloatSize ScrollingTreeFrameScrollingNodeMac::viewportSize() const
{
return scrollableAreaSize();
}
#endif
void ScrollingTreeFrameScrollingNodeMac::deferTestsForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) const
{
if (!m_expectsWheelEventTestTrigger)
return;
LOG(WheelEventTestTriggers, " ScrollingTreeFrameScrollingNodeMac::deferTestsForReason: STARTING deferral for %p because of %d", identifier, reason);
scrollingTree().deferTestsForReason(identifier, reason);
}
void ScrollingTreeFrameScrollingNodeMac::removeTestDeferralForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) const
{
if (!m_expectsWheelEventTestTrigger)
return;
LOG(WheelEventTestTriggers, " ScrollingTreeFrameScrollingNodeMac::deferTestsForReason: ENDING deferral for %p because of %d", identifier, reason);
scrollingTree().removeTestDeferralForReason(identifier, reason);
}
} // namespace WebCore
#endif // ENABLE(ASYNC_SCROLLING) && PLATFORM(MAC)