| 1 | /* |
| 2 | * Copyright (C) 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 | #include "config.h" |
| 27 | |
| 28 | #if ENABLE(ASYNC_SCROLLING) |
| 29 | #include "AsyncScrollingCoordinator.h" |
| 30 | |
| 31 | #include "DebugPageOverlays.h" |
| 32 | #include "Document.h" |
| 33 | #include "EditorClient.h" |
| 34 | #include "Frame.h" |
| 35 | #include "FrameView.h" |
| 36 | #include "GraphicsLayer.h" |
| 37 | #include "Logging.h" |
| 38 | #include "Page.h" |
| 39 | #include "PerformanceLoggingClient.h" |
| 40 | #include "ScrollAnimator.h" |
| 41 | #include "ScrollingConstraints.h" |
| 42 | #include "ScrollingStateFixedNode.h" |
| 43 | #include "ScrollingStateFrameHostingNode.h" |
| 44 | #include "ScrollingStateFrameScrollingNode.h" |
| 45 | #include "ScrollingStateOverflowScrollingNode.h" |
| 46 | #include "ScrollingStatePositionedNode.h" |
| 47 | #include "ScrollingStateStickyNode.h" |
| 48 | #include "ScrollingStateTree.h" |
| 49 | #include "Settings.h" |
| 50 | #include "WheelEventTestTrigger.h" |
| 51 | #include <wtf/ProcessID.h> |
| 52 | #include <wtf/text/TextStream.h> |
| 53 | |
| 54 | namespace WebCore { |
| 55 | |
| 56 | AsyncScrollingCoordinator::AsyncScrollingCoordinator(Page* page) |
| 57 | : ScrollingCoordinator(page) |
| 58 | , m_updateNodeScrollPositionTimer(*this, &AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScrollTimerFired) |
| 59 | , m_scrollingStateTree(std::make_unique<ScrollingStateTree>(this)) |
| 60 | { |
| 61 | } |
| 62 | |
| 63 | AsyncScrollingCoordinator::~AsyncScrollingCoordinator() = default; |
| 64 | |
| 65 | void AsyncScrollingCoordinator::scrollingStateTreePropertiesChanged() |
| 66 | { |
| 67 | scheduleTreeStateCommit(); |
| 68 | } |
| 69 | |
| 70 | #if ENABLE(CSS_SCROLL_SNAP) |
| 71 | static inline void setStateScrollingNodeSnapOffsetsAsFloat(ScrollingStateScrollingNode& node, ScrollEventAxis axis, const Vector<LayoutUnit>* snapOffsets, const Vector<ScrollOffsetRange<LayoutUnit>>* snapOffsetRanges, float deviceScaleFactor) |
| 72 | { |
| 73 | // FIXME: Incorporate current page scale factor in snapping to device pixel. Perhaps we should just convert to float here and let UI process do the pixel snapping? |
| 74 | Vector<float> snapOffsetsAsFloat; |
| 75 | if (snapOffsets) { |
| 76 | snapOffsetsAsFloat.reserveInitialCapacity(snapOffsets->size()); |
| 77 | for (auto& offset : *snapOffsets) |
| 78 | snapOffsetsAsFloat.uncheckedAppend(roundToDevicePixel(offset, deviceScaleFactor, false)); |
| 79 | } |
| 80 | |
| 81 | Vector<ScrollOffsetRange<float>> snapOffsetRangesAsFloat; |
| 82 | if (snapOffsetRanges) { |
| 83 | snapOffsetRangesAsFloat.reserveInitialCapacity(snapOffsetRanges->size()); |
| 84 | for (auto& range : *snapOffsetRanges) |
| 85 | snapOffsetRangesAsFloat.uncheckedAppend({ roundToDevicePixel(range.start, deviceScaleFactor, false), roundToDevicePixel(range.end, deviceScaleFactor, false) }); |
| 86 | } |
| 87 | if (axis == ScrollEventAxis::Horizontal) { |
| 88 | node.setHorizontalSnapOffsets(snapOffsetsAsFloat); |
| 89 | node.setHorizontalSnapOffsetRanges(snapOffsetRangesAsFloat); |
| 90 | } else { |
| 91 | node.setVerticalSnapOffsets(snapOffsetsAsFloat); |
| 92 | node.setVerticalSnapOffsetRanges(snapOffsetRangesAsFloat); |
| 93 | } |
| 94 | } |
| 95 | #endif |
| 96 | |
| 97 | void AsyncScrollingCoordinator::setEventTrackingRegionsDirty() |
| 98 | { |
| 99 | m_eventTrackingRegionsDirty = true; |
| 100 | // We have to schedule a commit, but the computed non-fast region may not have actually changed. |
| 101 | scheduleTreeStateCommit(); |
| 102 | } |
| 103 | |
| 104 | void AsyncScrollingCoordinator::willCommitTree() |
| 105 | { |
| 106 | updateEventTrackingRegions(); |
| 107 | } |
| 108 | |
| 109 | void AsyncScrollingCoordinator::updateEventTrackingRegions() |
| 110 | { |
| 111 | if (!m_eventTrackingRegionsDirty) |
| 112 | return; |
| 113 | |
| 114 | if (!m_scrollingStateTree->rootStateNode()) |
| 115 | return; |
| 116 | |
| 117 | m_scrollingStateTree->rootStateNode()->setEventTrackingRegions(absoluteEventTrackingRegions()); |
| 118 | m_eventTrackingRegionsDirty = false; |
| 119 | } |
| 120 | |
| 121 | void AsyncScrollingCoordinator::frameViewLayoutUpdated(FrameView& frameView) |
| 122 | { |
| 123 | ASSERT(isMainThread()); |
| 124 | ASSERT(m_page); |
| 125 | |
| 126 | // If there isn't a root node yet, don't do anything. We'll be called again after creating one. |
| 127 | if (!m_scrollingStateTree->rootStateNode()) |
| 128 | return; |
| 129 | |
| 130 | setEventTrackingRegionsDirty(); |
| 131 | |
| 132 | #if PLATFORM(COCOA) |
| 133 | if (!coordinatesScrollingForFrameView(frameView)) |
| 134 | return; |
| 135 | |
| 136 | auto* page = frameView.frame().page(); |
| 137 | if (page && page->expectsWheelEventTriggers()) { |
| 138 | LOG(WheelEventTestTriggers, " AsyncScrollingCoordinator::frameViewLayoutUpdated: Expects wheel event test trigger=%d" , page->expectsWheelEventTriggers()); |
| 139 | |
| 140 | auto* node = m_scrollingStateTree->stateNodeForID(frameView.scrollingNodeID()); |
| 141 | if (!is<ScrollingStateFrameScrollingNode>(node)) |
| 142 | return; |
| 143 | |
| 144 | auto& frameScrollingNode = downcast<ScrollingStateFrameScrollingNode>(*node); |
| 145 | frameScrollingNode.setExpectsWheelEventTestTrigger(page->expectsWheelEventTriggers()); |
| 146 | } |
| 147 | #else |
| 148 | UNUSED_PARAM(frameView); |
| 149 | #endif |
| 150 | } |
| 151 | |
| 152 | void AsyncScrollingCoordinator::updateExpectsWheelEventTestTriggerWithFrameView(const FrameView& frameView) |
| 153 | { |
| 154 | auto* page = frameView.frame().page(); |
| 155 | if (!page) |
| 156 | return; |
| 157 | |
| 158 | auto* node = downcast<ScrollingStateFrameScrollingNode>(m_scrollingStateTree->stateNodeForID(frameView.scrollingNodeID())); |
| 159 | if (!node) |
| 160 | return; |
| 161 | |
| 162 | node->setExpectsWheelEventTestTrigger(page->expectsWheelEventTriggers()); |
| 163 | } |
| 164 | |
| 165 | void AsyncScrollingCoordinator::frameViewEventTrackingRegionsChanged(FrameView& frameView) |
| 166 | { |
| 167 | if (!m_scrollingStateTree->rootStateNode()) |
| 168 | return; |
| 169 | |
| 170 | setEventTrackingRegionsDirty(); |
| 171 | DebugPageOverlays::didChangeEventHandlers(frameView.frame()); |
| 172 | } |
| 173 | |
| 174 | void AsyncScrollingCoordinator::frameViewRootLayerDidChange(FrameView& frameView) |
| 175 | { |
| 176 | ASSERT(isMainThread()); |
| 177 | ASSERT(m_page); |
| 178 | |
| 179 | if (!coordinatesScrollingForFrameView(frameView)) |
| 180 | return; |
| 181 | |
| 182 | // FIXME: In some navigation scenarios, the FrameView has no RenderView or that RenderView has not been composited. |
| 183 | // This needs cleaning up: https://bugs.webkit.org/show_bug.cgi?id=132724 |
| 184 | if (!frameView.scrollingNodeID()) |
| 185 | return; |
| 186 | |
| 187 | // If the root layer does not have a ScrollingStateNode, then we should create one. |
| 188 | ensureRootStateNodeForFrameView(frameView); |
| 189 | ASSERT(m_scrollingStateTree->stateNodeForID(frameView.scrollingNodeID())); |
| 190 | |
| 191 | ScrollingCoordinator::frameViewRootLayerDidChange(frameView); |
| 192 | |
| 193 | auto* node = downcast<ScrollingStateFrameScrollingNode>(m_scrollingStateTree->stateNodeForID(frameView.scrollingNodeID())); |
| 194 | node->setScrollContainerLayer(scrollContainerLayerForFrameView(frameView)); |
| 195 | node->setScrolledContentsLayer(scrolledContentsLayerForFrameView(frameView)); |
| 196 | node->setRootContentsLayer(rootContentsLayerForFrameView(frameView)); |
| 197 | node->setCounterScrollingLayer(counterScrollingLayerForFrameView(frameView)); |
| 198 | node->setInsetClipLayer(insetClipLayerForFrameView(frameView)); |
| 199 | node->setContentShadowLayer(contentShadowLayerForFrameView(frameView)); |
| 200 | node->setHeaderLayer(headerLayerForFrameView(frameView)); |
| 201 | node->setFooterLayer(footerLayerForFrameView(frameView)); |
| 202 | node->setScrollBehaviorForFixedElements(frameView.scrollBehaviorForFixedElements()); |
| 203 | node->setVerticalScrollbarLayer(frameView.layerForVerticalScrollbar()); |
| 204 | node->setHorizontalScrollbarLayer(frameView.layerForHorizontalScrollbar()); |
| 205 | } |
| 206 | |
| 207 | bool AsyncScrollingCoordinator::requestScrollPositionUpdate(ScrollableArea& scrollableArea, const IntPoint& scrollPosition) |
| 208 | { |
| 209 | ASSERT(isMainThread()); |
| 210 | ASSERT(m_page); |
| 211 | |
| 212 | auto scrollingNodeID = scrollableArea.scrollingNodeID(); |
| 213 | if (!scrollingNodeID) |
| 214 | return false; |
| 215 | |
| 216 | auto* frameView = frameViewForScrollingNode(scrollingNodeID); |
| 217 | if (!frameView) |
| 218 | return false; |
| 219 | |
| 220 | if (!coordinatesScrollingForFrameView(*frameView)) |
| 221 | return false; |
| 222 | |
| 223 | bool inPageCache = frameView->frame().document()->pageCacheState() != Document::NotInPageCache; |
| 224 | bool inProgrammaticScroll = scrollableArea.currentScrollType() == ScrollType::Programmatic; |
| 225 | if (inProgrammaticScroll || inPageCache) |
| 226 | updateScrollPositionAfterAsyncScroll(scrollingNodeID, scrollPosition, { }, ScrollType::Programmatic, ScrollingLayerPositionAction::Set); |
| 227 | |
| 228 | // If this frame view's document is being put into the page cache, we don't want to update our |
| 229 | // main frame scroll position. Just let the FrameView think that we did. |
| 230 | if (inPageCache) |
| 231 | return true; |
| 232 | |
| 233 | auto* stateNode = downcast<ScrollingStateScrollingNode>(m_scrollingStateTree->stateNodeForID(scrollingNodeID)); |
| 234 | if (!stateNode) |
| 235 | return false; |
| 236 | |
| 237 | stateNode->setRequestedScrollPosition(scrollPosition, inProgrammaticScroll); |
| 238 | return true; |
| 239 | } |
| 240 | |
| 241 | void AsyncScrollingCoordinator::applyScrollingTreeLayerPositions() |
| 242 | { |
| 243 | m_scrollingTree->applyLayerPositions(); |
| 244 | } |
| 245 | |
| 246 | void AsyncScrollingCoordinator::scheduleUpdateScrollPositionAfterAsyncScroll(ScrollingNodeID nodeID, const FloatPoint& scrollPosition, const Optional<FloatPoint>& layoutViewportOrigin, ScrollingLayerPositionAction scrollingLayerPositionAction) |
| 247 | { |
| 248 | ScheduledScrollUpdate scrollUpdate(nodeID, scrollPosition, layoutViewportOrigin, scrollingLayerPositionAction); |
| 249 | |
| 250 | if (m_updateNodeScrollPositionTimer.isActive()) { |
| 251 | if (m_scheduledScrollUpdate.matchesUpdateType(scrollUpdate)) { |
| 252 | m_scheduledScrollUpdate.scrollPosition = scrollPosition; |
| 253 | m_scheduledScrollUpdate.layoutViewportOrigin = layoutViewportOrigin; |
| 254 | return; |
| 255 | } |
| 256 | |
| 257 | // If the parameters don't match what was previously scheduled, dispatch immediately. |
| 258 | m_updateNodeScrollPositionTimer.stop(); |
| 259 | updateScrollPositionAfterAsyncScroll(m_scheduledScrollUpdate.nodeID, m_scheduledScrollUpdate.scrollPosition, m_scheduledScrollUpdate.layoutViewportOrigin, ScrollType::User, m_scheduledScrollUpdate.updateLayerPositionAction); |
| 260 | updateScrollPositionAfterAsyncScroll(nodeID, scrollPosition, layoutViewportOrigin, ScrollType::User, scrollingLayerPositionAction); |
| 261 | return; |
| 262 | } |
| 263 | |
| 264 | m_scheduledScrollUpdate = scrollUpdate; |
| 265 | m_updateNodeScrollPositionTimer.startOneShot(0_s); |
| 266 | } |
| 267 | |
| 268 | void AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScrollTimerFired() |
| 269 | { |
| 270 | updateScrollPositionAfterAsyncScroll(m_scheduledScrollUpdate.nodeID, m_scheduledScrollUpdate.scrollPosition, m_scheduledScrollUpdate.layoutViewportOrigin, ScrollType::User, m_scheduledScrollUpdate.updateLayerPositionAction); |
| 271 | } |
| 272 | |
| 273 | FrameView* AsyncScrollingCoordinator::frameViewForScrollingNode(ScrollingNodeID scrollingNodeID) const |
| 274 | { |
| 275 | if (!m_scrollingStateTree->rootStateNode()) |
| 276 | return nullptr; |
| 277 | |
| 278 | if (scrollingNodeID == m_scrollingStateTree->rootStateNode()->scrollingNodeID()) |
| 279 | return m_page->mainFrame().view(); |
| 280 | |
| 281 | auto* stateNode = m_scrollingStateTree->stateNodeForID(scrollingNodeID); |
| 282 | if (!stateNode) |
| 283 | return nullptr; |
| 284 | |
| 285 | // Find the enclosing frame scrolling node. |
| 286 | auto* parentNode = stateNode; |
| 287 | while (parentNode && !parentNode->isFrameScrollingNode()) |
| 288 | parentNode = parentNode->parent(); |
| 289 | |
| 290 | if (!parentNode) |
| 291 | return nullptr; |
| 292 | |
| 293 | // Walk the frame tree to find the matching FrameView. This is not ideal, but avoids back pointers to FrameViews |
| 294 | // from ScrollingTreeStateNodes. |
| 295 | for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) { |
| 296 | if (auto* view = frame->view()) { |
| 297 | if (view->scrollingNodeID() == parentNode->scrollingNodeID()) |
| 298 | return view; |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | return nullptr; |
| 303 | } |
| 304 | |
| 305 | void AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScroll(ScrollingNodeID scrollingNodeID, const FloatPoint& scrollPosition, Optional<FloatPoint> layoutViewportOrigin, ScrollType scrollType, ScrollingLayerPositionAction scrollingLayerPositionAction) |
| 306 | { |
| 307 | ASSERT(isMainThread()); |
| 308 | |
| 309 | if (!m_page) |
| 310 | return; |
| 311 | |
| 312 | auto* frameViewPtr = frameViewForScrollingNode(scrollingNodeID); |
| 313 | if (!frameViewPtr) |
| 314 | return; |
| 315 | |
| 316 | LOG_WITH_STREAM(Scrolling, stream << "AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScroll node " << scrollingNodeID << " scrollPosition " << scrollPosition << " action " << scrollingLayerPositionAction); |
| 317 | |
| 318 | auto& frameView = *frameViewPtr; |
| 319 | |
| 320 | if (scrollingNodeID == frameView.scrollingNodeID()) { |
| 321 | reconcileScrollingState(frameView, scrollPosition, layoutViewportOrigin, scrollType, ViewportRectStability::Stable, scrollingLayerPositionAction); |
| 322 | |
| 323 | #if PLATFORM(COCOA) |
| 324 | if (m_page->expectsWheelEventTriggers()) { |
| 325 | frameView.scrollAnimator().setWheelEventTestTrigger(m_page->testTrigger()); |
| 326 | if (const auto& trigger = m_page->testTrigger()) |
| 327 | trigger->removeTestDeferralForReason(reinterpret_cast<WheelEventTestTrigger::ScrollableAreaIdentifier>(scrollingNodeID), WheelEventTestTrigger::ScrollingThreadSyncNeeded); |
| 328 | } |
| 329 | #endif |
| 330 | |
| 331 | return; |
| 332 | } |
| 333 | |
| 334 | // Overflow-scroll area. |
| 335 | if (auto* scrollableArea = frameView.scrollableAreaForScrollLayerID(scrollingNodeID)) { |
| 336 | auto previousScrollType = scrollableArea->currentScrollType(); |
| 337 | scrollableArea->setCurrentScrollType(scrollType); |
| 338 | scrollableArea->scrollToOffsetWithoutAnimation(scrollPosition); |
| 339 | scrollableArea->setCurrentScrollType(previousScrollType); |
| 340 | |
| 341 | if (scrollingLayerPositionAction == ScrollingLayerPositionAction::Set) |
| 342 | m_page->editorClient().overflowScrollPositionChanged(); |
| 343 | |
| 344 | #if PLATFORM(COCOA) |
| 345 | if (m_page->expectsWheelEventTriggers()) { |
| 346 | frameView.scrollAnimator().setWheelEventTestTrigger(m_page->testTrigger()); |
| 347 | if (const auto& trigger = m_page->testTrigger()) |
| 348 | trigger->removeTestDeferralForReason(reinterpret_cast<WheelEventTestTrigger::ScrollableAreaIdentifier>(scrollingNodeID), WheelEventTestTrigger::ScrollingThreadSyncNeeded); |
| 349 | } |
| 350 | #endif |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | void AsyncScrollingCoordinator::reconcileScrollingState(FrameView& frameView, const FloatPoint& scrollPosition, const LayoutViewportOriginOrOverrideRect& layoutViewportOriginOrOverrideRect, ScrollType scrollType, ViewportRectStability viewportRectStability, ScrollingLayerPositionAction scrollingLayerPositionAction) |
| 355 | { |
| 356 | auto previousScrollType = frameView.currentScrollType(); |
| 357 | frameView.setCurrentScrollType(scrollType); |
| 358 | |
| 359 | LOG_WITH_STREAM(Scrolling, stream << getCurrentProcessID() << " AsyncScrollingCoordinator " << this << " reconcileScrollingState scrollPosition " << scrollPosition << " type " << scrollType << " stability " << viewportRectStability << " " << scrollingLayerPositionAction); |
| 360 | |
| 361 | Optional<FloatRect> layoutViewportRect; |
| 362 | |
| 363 | WTF::switchOn(layoutViewportOriginOrOverrideRect, |
| 364 | [&frameView](Optional<FloatPoint> origin) { |
| 365 | if (origin) |
| 366 | frameView.setBaseLayoutViewportOrigin(LayoutPoint(origin.value()), FrameView::TriggerLayoutOrNot::No); |
| 367 | }, [&frameView, &layoutViewportRect, viewportRectStability](Optional<FloatRect> overrideRect) { |
| 368 | if (!overrideRect) |
| 369 | return; |
| 370 | |
| 371 | layoutViewportRect = overrideRect; |
| 372 | if (viewportRectStability != ViewportRectStability::ChangingObscuredInsetsInteractively) |
| 373 | frameView.setLayoutViewportOverrideRect(LayoutRect(overrideRect.value()), viewportRectStability == ViewportRectStability::Stable ? FrameView::TriggerLayoutOrNot::Yes : FrameView::TriggerLayoutOrNot::No); |
| 374 | } |
| 375 | ); |
| 376 | |
| 377 | frameView.setConstrainsScrollingToContentEdge(false); |
| 378 | frameView.notifyScrollPositionChanged(roundedIntPoint(scrollPosition)); |
| 379 | frameView.setConstrainsScrollingToContentEdge(true); |
| 380 | |
| 381 | frameView.setCurrentScrollType(previousScrollType); |
| 382 | |
| 383 | if (scrollType == ScrollType::User && scrollingLayerPositionAction != ScrollingLayerPositionAction::Set) { |
| 384 | auto scrollingNodeID = frameView.scrollingNodeID(); |
| 385 | if (viewportRectStability == ViewportRectStability::Stable) |
| 386 | reconcileViewportConstrainedLayerPositions(scrollingNodeID, frameView.rectForFixedPositionLayout(), scrollingLayerPositionAction); |
| 387 | else if (layoutViewportRect) |
| 388 | reconcileViewportConstrainedLayerPositions(scrollingNodeID, LayoutRect(layoutViewportRect.value()), scrollingLayerPositionAction); |
| 389 | } |
| 390 | |
| 391 | if (!scrolledContentsLayerForFrameView(frameView)) |
| 392 | return; |
| 393 | |
| 394 | auto* counterScrollingLayer = counterScrollingLayerForFrameView(frameView); |
| 395 | auto* insetClipLayer = insetClipLayerForFrameView(frameView); |
| 396 | auto* contentShadowLayer = contentShadowLayerForFrameView(frameView); |
| 397 | auto* rootContentsLayer = rootContentsLayerForFrameView(frameView); |
| 398 | auto* = headerLayerForFrameView(frameView); |
| 399 | auto* = footerLayerForFrameView(frameView); |
| 400 | |
| 401 | ASSERT(frameView.scrollPosition() == roundedIntPoint(scrollPosition)); |
| 402 | LayoutPoint scrollPositionForFixed = frameView.scrollPositionForFixedPosition(); |
| 403 | float topContentInset = frameView.topContentInset(); |
| 404 | |
| 405 | FloatPoint positionForInsetClipLayer; |
| 406 | if (insetClipLayer) |
| 407 | positionForInsetClipLayer = FloatPoint(insetClipLayer->position().x(), FrameView::yPositionForInsetClipLayer(scrollPosition, topContentInset)); |
| 408 | FloatPoint positionForContentsLayer = frameView.positionForRootContentLayer(); |
| 409 | |
| 410 | FloatPoint = FloatPoint(scrollPositionForFixed.x(), FrameView::yPositionForHeaderLayer(scrollPosition, topContentInset)); |
| 411 | FloatPoint = FloatPoint(scrollPositionForFixed.x(), |
| 412 | FrameView::yPositionForFooterLayer(scrollPosition, topContentInset, frameView.totalContentsSize().height(), frameView.footerHeight())); |
| 413 | |
| 414 | if (scrollType == ScrollType::Programmatic || scrollingLayerPositionAction == ScrollingLayerPositionAction::Set) { |
| 415 | reconcileScrollPosition(frameView, ScrollingLayerPositionAction::Set); |
| 416 | |
| 417 | if (counterScrollingLayer) |
| 418 | counterScrollingLayer->setPosition(scrollPositionForFixed); |
| 419 | if (insetClipLayer) |
| 420 | insetClipLayer->setPosition(positionForInsetClipLayer); |
| 421 | if (contentShadowLayer) |
| 422 | contentShadowLayer->setPosition(positionForContentsLayer); |
| 423 | if (rootContentsLayer) |
| 424 | rootContentsLayer->setPosition(positionForContentsLayer); |
| 425 | if (headerLayer) |
| 426 | headerLayer->setPosition(positionForHeaderLayer); |
| 427 | if (footerLayer) |
| 428 | footerLayer->setPosition(positionForFooterLayer); |
| 429 | } else { |
| 430 | reconcileScrollPosition(frameView, ScrollingLayerPositionAction::Sync); |
| 431 | |
| 432 | if (counterScrollingLayer) |
| 433 | counterScrollingLayer->syncPosition(scrollPositionForFixed); |
| 434 | if (insetClipLayer) |
| 435 | insetClipLayer->syncPosition(positionForInsetClipLayer); |
| 436 | if (contentShadowLayer) |
| 437 | contentShadowLayer->syncPosition(positionForContentsLayer); |
| 438 | if (rootContentsLayer) |
| 439 | rootContentsLayer->syncPosition(positionForContentsLayer); |
| 440 | if (headerLayer) |
| 441 | headerLayer->syncPosition(positionForHeaderLayer); |
| 442 | if (footerLayer) |
| 443 | footerLayer->syncPosition(positionForFooterLayer); |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | void AsyncScrollingCoordinator::reconcileScrollPosition(FrameView& frameView, ScrollingLayerPositionAction scrollingLayerPositionAction) |
| 448 | { |
| 449 | #if PLATFORM(IOS_FAMILY) |
| 450 | // Doing all scrolling like this (UIScrollView style) would simplify code. |
| 451 | auto* scrollContainerLayer = scrollContainerLayerForFrameView(frameView); |
| 452 | if (!scrollContainerLayer) |
| 453 | return; |
| 454 | if (scrollingLayerPositionAction == ScrollingLayerPositionAction::Set) |
| 455 | scrollContainerLayer->setBoundsOrigin(frameView.scrollPosition()); |
| 456 | else |
| 457 | scrollContainerLayer->syncBoundsOrigin(frameView.scrollPosition()); |
| 458 | #else |
| 459 | auto* scrolledContentsLayer = scrolledContentsLayerForFrameView(frameView); |
| 460 | if (!scrolledContentsLayer) |
| 461 | return; |
| 462 | if (scrollingLayerPositionAction == ScrollingLayerPositionAction::Set) |
| 463 | scrolledContentsLayer->setPosition(-frameView.scrollPosition()); |
| 464 | else |
| 465 | scrolledContentsLayer->syncPosition(-frameView.scrollPosition()); |
| 466 | #endif |
| 467 | } |
| 468 | |
| 469 | void AsyncScrollingCoordinator::scrollableAreaScrollbarLayerDidChange(ScrollableArea& scrollableArea, ScrollbarOrientation orientation) |
| 470 | { |
| 471 | ASSERT(isMainThread()); |
| 472 | ASSERT(m_page); |
| 473 | |
| 474 | auto* node = m_scrollingStateTree->stateNodeForID(scrollableArea.scrollingNodeID()); |
| 475 | if (is<ScrollingStateScrollingNode>(node)) { |
| 476 | auto& scrollingNode = downcast<ScrollingStateScrollingNode>(*node); |
| 477 | if (orientation == VerticalScrollbar) |
| 478 | scrollingNode.setVerticalScrollbarLayer(scrollableArea.layerForVerticalScrollbar()); |
| 479 | else |
| 480 | scrollingNode.setHorizontalScrollbarLayer(scrollableArea.layerForHorizontalScrollbar()); |
| 481 | } |
| 482 | |
| 483 | if (orientation == VerticalScrollbar) |
| 484 | scrollableArea.verticalScrollbarLayerDidChange(); |
| 485 | else |
| 486 | scrollableArea.horizontalScrollbarLayerDidChange(); |
| 487 | } |
| 488 | |
| 489 | ScrollingNodeID AsyncScrollingCoordinator::createNode(ScrollingNodeType nodeType, ScrollingNodeID newNodeID) |
| 490 | { |
| 491 | LOG_WITH_STREAM(Scrolling, stream << "AsyncScrollingCoordinator::createNode " << nodeType << " node " << newNodeID); |
| 492 | return m_scrollingStateTree->createUnparentedNode(nodeType, newNodeID); |
| 493 | } |
| 494 | |
| 495 | ScrollingNodeID AsyncScrollingCoordinator::insertNode(ScrollingNodeType nodeType, ScrollingNodeID newNodeID, ScrollingNodeID parentID, size_t childIndex) |
| 496 | { |
| 497 | LOG_WITH_STREAM(Scrolling, stream << "AsyncScrollingCoordinator::insertNode " << nodeType << " node " << newNodeID << " parent " << parentID << " index " << childIndex); |
| 498 | return m_scrollingStateTree->insertNode(nodeType, newNodeID, parentID, childIndex); |
| 499 | } |
| 500 | |
| 501 | void AsyncScrollingCoordinator::unparentNode(ScrollingNodeID nodeID) |
| 502 | { |
| 503 | m_scrollingStateTree->unparentNode(nodeID); |
| 504 | } |
| 505 | |
| 506 | void AsyncScrollingCoordinator::unparentChildrenAndDestroyNode(ScrollingNodeID nodeID) |
| 507 | { |
| 508 | m_scrollingStateTree->unparentChildrenAndDestroyNode(nodeID); |
| 509 | } |
| 510 | |
| 511 | void AsyncScrollingCoordinator::detachAndDestroySubtree(ScrollingNodeID nodeID) |
| 512 | { |
| 513 | m_scrollingStateTree->detachAndDestroySubtree(nodeID); |
| 514 | } |
| 515 | |
| 516 | void AsyncScrollingCoordinator::clearAllNodes() |
| 517 | { |
| 518 | m_scrollingStateTree->clear(); |
| 519 | } |
| 520 | |
| 521 | ScrollingNodeID AsyncScrollingCoordinator::parentOfNode(ScrollingNodeID nodeID) const |
| 522 | { |
| 523 | auto* scrollingNode = m_scrollingStateTree->stateNodeForID(nodeID); |
| 524 | if (!scrollingNode) |
| 525 | return 0; |
| 526 | |
| 527 | return scrollingNode->parentNodeID(); |
| 528 | } |
| 529 | |
| 530 | Vector<ScrollingNodeID> AsyncScrollingCoordinator::childrenOfNode(ScrollingNodeID nodeID) const |
| 531 | { |
| 532 | auto* scrollingNode = m_scrollingStateTree->stateNodeForID(nodeID); |
| 533 | if (!scrollingNode) |
| 534 | return { }; |
| 535 | |
| 536 | auto* children = scrollingNode->children(); |
| 537 | if (!children || children->isEmpty()) |
| 538 | return { }; |
| 539 | |
| 540 | Vector<ScrollingNodeID> childNodeIDs; |
| 541 | childNodeIDs.reserveInitialCapacity(children->size()); |
| 542 | for (const auto& childNode : *children) |
| 543 | childNodeIDs.uncheckedAppend(childNode->scrollingNodeID()); |
| 544 | |
| 545 | return childNodeIDs; |
| 546 | } |
| 547 | |
| 548 | void AsyncScrollingCoordinator::reconcileViewportConstrainedLayerPositions(ScrollingNodeID scrollingNodeID, const LayoutRect& viewportRect, ScrollingLayerPositionAction action) |
| 549 | { |
| 550 | LOG_WITH_STREAM(Scrolling, stream << getCurrentProcessID() << " AsyncScrollingCoordinator::reconcileViewportConstrainedLayerPositions for viewport rect " << viewportRect << " and node " << scrollingNodeID); |
| 551 | |
| 552 | m_scrollingStateTree->reconcileViewportConstrainedLayerPositions(scrollingNodeID, viewportRect, action); |
| 553 | } |
| 554 | |
| 555 | void AsyncScrollingCoordinator::ensureRootStateNodeForFrameView(FrameView& frameView) |
| 556 | { |
| 557 | ASSERT(frameView.scrollingNodeID()); |
| 558 | if (m_scrollingStateTree->stateNodeForID(frameView.scrollingNodeID())) |
| 559 | return; |
| 560 | |
| 561 | // For non-main frames, it is only possible to arrive in this function from |
| 562 | // RenderLayerCompositor::updateBacking where the node has already been created. |
| 563 | ASSERT(frameView.frame().isMainFrame()); |
| 564 | insertNode(ScrollingNodeType::MainFrame, frameView.scrollingNodeID(), 0, 0); |
| 565 | } |
| 566 | |
| 567 | void AsyncScrollingCoordinator::setNodeLayers(ScrollingNodeID nodeID, const NodeLayers& nodeLayers) |
| 568 | { |
| 569 | auto* node = m_scrollingStateTree->stateNodeForID(nodeID); |
| 570 | ASSERT(node); |
| 571 | if (!node) |
| 572 | return; |
| 573 | |
| 574 | node->setLayer(nodeLayers.layer); |
| 575 | |
| 576 | if (is<ScrollingStateScrollingNode>(node)) { |
| 577 | auto& scrollingNode = downcast<ScrollingStateScrollingNode>(*node); |
| 578 | scrollingNode.setScrollContainerLayer(nodeLayers.scrollContainerLayer); |
| 579 | scrollingNode.setScrolledContentsLayer(nodeLayers.scrolledContentsLayer); |
| 580 | scrollingNode.setHorizontalScrollbarLayer(nodeLayers.horizontalScrollbarLayer); |
| 581 | scrollingNode.setVerticalScrollbarLayer(nodeLayers.verticalScrollbarLayer); |
| 582 | |
| 583 | if (is<ScrollingStateFrameScrollingNode>(node)) { |
| 584 | auto& frameScrollingNode = downcast<ScrollingStateFrameScrollingNode>(*node); |
| 585 | frameScrollingNode.setInsetClipLayer(nodeLayers.insetClipLayer); |
| 586 | frameScrollingNode.setCounterScrollingLayer(nodeLayers.counterScrollingLayer); |
| 587 | frameScrollingNode.setRootContentsLayer(nodeLayers.rootContentsLayer); |
| 588 | } |
| 589 | } |
| 590 | } |
| 591 | |
| 592 | void AsyncScrollingCoordinator::setRectRelativeToParentNode(ScrollingNodeID nodeID, const LayoutRect& parentRelativeScrollableRect) |
| 593 | { |
| 594 | auto* stateNode = m_scrollingStateTree->stateNodeForID(nodeID); |
| 595 | ASSERT(stateNode); |
| 596 | if (!stateNode) |
| 597 | return; |
| 598 | |
| 599 | if (is<ScrollingStateFrameHostingNode>(*stateNode)) { |
| 600 | auto& frameHostingStateNode = downcast<ScrollingStateFrameHostingNode>(*stateNode); |
| 601 | frameHostingStateNode.setParentRelativeScrollableRect(parentRelativeScrollableRect); |
| 602 | return; |
| 603 | } |
| 604 | |
| 605 | if (is<ScrollingStateScrollingNode>(stateNode)) { |
| 606 | auto& scrollingStateNode = downcast<ScrollingStateScrollingNode>(*stateNode); |
| 607 | scrollingStateNode.setParentRelativeScrollableRect(parentRelativeScrollableRect); |
| 608 | } |
| 609 | } |
| 610 | |
| 611 | void AsyncScrollingCoordinator::setFrameScrollingNodeState(ScrollingNodeID nodeID, const FrameView& frameView) |
| 612 | { |
| 613 | auto* stateNode = m_scrollingStateTree->stateNodeForID(nodeID); |
| 614 | ASSERT(stateNode); |
| 615 | if (!is<ScrollingStateFrameScrollingNode>(stateNode)) |
| 616 | return; |
| 617 | |
| 618 | auto& frameScrollingNode = downcast<ScrollingStateFrameScrollingNode>(*stateNode); |
| 619 | |
| 620 | frameScrollingNode.setFrameScaleFactor(frameView.frame().frameScaleFactor()); |
| 621 | frameScrollingNode.setHeaderHeight(frameView.headerHeight()); |
| 622 | frameScrollingNode.setFooterHeight(frameView.footerHeight()); |
| 623 | frameScrollingNode.setTopContentInset(frameView.topContentInset()); |
| 624 | frameScrollingNode.setLayoutViewport(frameView.layoutViewportRect()); |
| 625 | frameScrollingNode.setAsyncFrameOrOverflowScrollingEnabled(asyncFrameOrOverflowScrollingEnabled()); |
| 626 | |
| 627 | frameScrollingNode.setMinLayoutViewportOrigin(frameView.minStableLayoutViewportOrigin()); |
| 628 | frameScrollingNode.setMaxLayoutViewportOrigin(frameView.maxStableLayoutViewportOrigin()); |
| 629 | |
| 630 | frameScrollingNode.setFixedElementsLayoutRelativeToFrame(frameView.fixedElementsLayoutRelativeToFrame()); |
| 631 | frameScrollingNode.setScrollBehaviorForFixedElements(frameView.scrollBehaviorForFixedElements()); |
| 632 | } |
| 633 | |
| 634 | void AsyncScrollingCoordinator::setScrollingNodeScrollableAreaGeometry(ScrollingNodeID nodeID, ScrollableArea& scrollableArea) |
| 635 | { |
| 636 | auto* stateNode = m_scrollingStateTree->stateNodeForID(nodeID); |
| 637 | ASSERT(stateNode); |
| 638 | if (!stateNode) |
| 639 | return; |
| 640 | |
| 641 | auto& scrollingNode = downcast<ScrollingStateScrollingNode>(*stateNode); |
| 642 | |
| 643 | auto* verticalScrollbar = scrollableArea.verticalScrollbar(); |
| 644 | auto* horizontalScrollbar = scrollableArea.horizontalScrollbar(); |
| 645 | scrollingNode.setScrollerImpsFromScrollbars(verticalScrollbar, horizontalScrollbar); |
| 646 | |
| 647 | scrollingNode.setScrollOrigin(scrollableArea.scrollOrigin()); |
| 648 | scrollingNode.setScrollPosition(scrollableArea.scrollPosition()); |
| 649 | scrollingNode.setTotalContentsSize(scrollableArea.totalContentsSize()); |
| 650 | scrollingNode.setReachableContentsSize(scrollableArea.reachableTotalContentsSize()); |
| 651 | scrollingNode.setScrollableAreaSize(scrollableArea.visibleSize()); |
| 652 | |
| 653 | ScrollableAreaParameters scrollParameters; |
| 654 | scrollParameters.horizontalScrollElasticity = scrollableArea.horizontalScrollElasticity(); |
| 655 | scrollParameters.verticalScrollElasticity = scrollableArea.verticalScrollElasticity(); |
| 656 | scrollParameters.hasEnabledHorizontalScrollbar = horizontalScrollbar && horizontalScrollbar->enabled(); |
| 657 | scrollParameters.hasEnabledVerticalScrollbar = verticalScrollbar && verticalScrollbar->enabled(); |
| 658 | scrollParameters.horizontalScrollbarMode = scrollableArea.horizontalScrollbarMode(); |
| 659 | scrollParameters.verticalScrollbarMode = scrollableArea.verticalScrollbarMode(); |
| 660 | scrollParameters.horizontalScrollbarHiddenByStyle = scrollableArea.horizontalScrollbarHiddenByStyle(); |
| 661 | scrollParameters.verticalScrollbarHiddenByStyle = scrollableArea.verticalScrollbarHiddenByStyle(); |
| 662 | scrollParameters.useDarkAppearanceForScrollbars = scrollableArea.useDarkAppearanceForScrollbars(); |
| 663 | |
| 664 | scrollingNode.setScrollableAreaParameters(scrollParameters); |
| 665 | |
| 666 | #if ENABLE(CSS_SCROLL_SNAP) |
| 667 | scrollableArea.updateSnapOffsets(); |
| 668 | setStateScrollingNodeSnapOffsetsAsFloat(scrollingNode, ScrollEventAxis::Horizontal, scrollableArea.horizontalSnapOffsets(), scrollableArea.horizontalSnapOffsetRanges(), m_page->deviceScaleFactor()); |
| 669 | setStateScrollingNodeSnapOffsetsAsFloat(scrollingNode, ScrollEventAxis::Vertical, scrollableArea.verticalSnapOffsets(), scrollableArea.verticalSnapOffsetRanges(), m_page->deviceScaleFactor()); |
| 670 | scrollingNode.setCurrentHorizontalSnapPointIndex(scrollableArea.currentHorizontalSnapPointIndex()); |
| 671 | scrollingNode.setCurrentVerticalSnapPointIndex(scrollableArea.currentVerticalSnapPointIndex()); |
| 672 | #endif |
| 673 | } |
| 674 | |
| 675 | void AsyncScrollingCoordinator::setViewportConstraintedNodeConstraints(ScrollingNodeID nodeID, const ViewportConstraints& constraints) |
| 676 | { |
| 677 | auto* node = m_scrollingStateTree->stateNodeForID(nodeID); |
| 678 | if (!node) |
| 679 | return; |
| 680 | |
| 681 | switch (constraints.constraintType()) { |
| 682 | case ViewportConstraints::FixedPositionConstraint: { |
| 683 | auto& fixedNode = downcast<ScrollingStateFixedNode>(*node); |
| 684 | fixedNode.updateConstraints((const FixedPositionViewportConstraints&)constraints); |
| 685 | break; |
| 686 | } |
| 687 | case ViewportConstraints::StickyPositionConstraint: { |
| 688 | auto& stickyNode = downcast<ScrollingStateStickyNode>(*node); |
| 689 | stickyNode.updateConstraints((const StickyPositionViewportConstraints&)constraints); |
| 690 | break; |
| 691 | } |
| 692 | } |
| 693 | } |
| 694 | |
| 695 | void AsyncScrollingCoordinator::setPositionedNodeGeometry(ScrollingNodeID nodeID, const LayoutConstraints& constraints) |
| 696 | { |
| 697 | auto* node = m_scrollingStateTree->stateNodeForID(nodeID); |
| 698 | if (!node) |
| 699 | return; |
| 700 | |
| 701 | ASSERT(is<ScrollingStatePositionedNode>(*node)); |
| 702 | if (auto* positionedNode = downcast<ScrollingStatePositionedNode>(node)) |
| 703 | positionedNode->updateConstraints(constraints); |
| 704 | } |
| 705 | |
| 706 | void AsyncScrollingCoordinator::setRelatedOverflowScrollingNodes(ScrollingNodeID nodeID, Vector<ScrollingNodeID>&& relatedNodes) |
| 707 | { |
| 708 | auto* node = m_scrollingStateTree->stateNodeForID(nodeID); |
| 709 | if (!node) |
| 710 | return; |
| 711 | |
| 712 | ASSERT(is<ScrollingStatePositionedNode>(*node)); |
| 713 | if (auto* positionedNode = downcast<ScrollingStatePositionedNode>(node)) |
| 714 | positionedNode->setRelatedOverflowScrollingNodes(WTFMove(relatedNodes)); |
| 715 | } |
| 716 | |
| 717 | void AsyncScrollingCoordinator::setSynchronousScrollingReasons(FrameView& frameView, SynchronousScrollingReasons reasons) |
| 718 | { |
| 719 | auto* scrollingStateNode = static_cast<ScrollingStateFrameScrollingNode*>(m_scrollingStateTree->stateNodeForID(frameView.scrollingNodeID())); |
| 720 | if (!scrollingStateNode) |
| 721 | return; |
| 722 | |
| 723 | // The FrameView's GraphicsLayer is likely to be out-of-synch with the PlatformLayer |
| 724 | // at this point. So we'll update it before we switch back to main thread scrolling |
| 725 | // in order to avoid layer positioning bugs. |
| 726 | if (reasons) |
| 727 | reconcileScrollPosition(frameView, ScrollingLayerPositionAction::Set); |
| 728 | scrollingStateNode->setSynchronousScrollingReasons(reasons); |
| 729 | } |
| 730 | |
| 731 | bool AsyncScrollingCoordinator::isRubberBandInProgress() const |
| 732 | { |
| 733 | return scrollingTree()->isRubberBandInProgress(); |
| 734 | } |
| 735 | |
| 736 | void AsyncScrollingCoordinator::setScrollPinningBehavior(ScrollPinningBehavior pinning) |
| 737 | { |
| 738 | scrollingTree()->setScrollPinningBehavior(pinning); |
| 739 | } |
| 740 | |
| 741 | bool AsyncScrollingCoordinator::asyncFrameOrOverflowScrollingEnabled() const |
| 742 | { |
| 743 | auto& settings = m_page->mainFrame().settings(); |
| 744 | return settings.asyncFrameScrollingEnabled() || settings.asyncOverflowScrollingEnabled(); |
| 745 | } |
| 746 | |
| 747 | String AsyncScrollingCoordinator::scrollingStateTreeAsText(ScrollingStateTreeAsTextBehavior behavior) const |
| 748 | { |
| 749 | if (m_scrollingStateTree->rootStateNode()) { |
| 750 | if (m_eventTrackingRegionsDirty) |
| 751 | m_scrollingStateTree->rootStateNode()->setEventTrackingRegions(absoluteEventTrackingRegions()); |
| 752 | return m_scrollingStateTree->rootStateNode()->scrollingStateTreeAsText(behavior); |
| 753 | } |
| 754 | |
| 755 | return String(); |
| 756 | } |
| 757 | |
| 758 | #if PLATFORM(COCOA) |
| 759 | void AsyncScrollingCoordinator::setActiveScrollSnapIndices(ScrollingNodeID scrollingNodeID, unsigned horizontalIndex, unsigned verticalIndex) |
| 760 | { |
| 761 | ASSERT(isMainThread()); |
| 762 | |
| 763 | if (!m_page) |
| 764 | return; |
| 765 | |
| 766 | auto* frameView = frameViewForScrollingNode(scrollingNodeID); |
| 767 | if (!frameView) |
| 768 | return; |
| 769 | |
| 770 | if (scrollingNodeID == frameView->scrollingNodeID()) { |
| 771 | frameView->setCurrentHorizontalSnapPointIndex(horizontalIndex); |
| 772 | frameView->setCurrentVerticalSnapPointIndex(verticalIndex); |
| 773 | return; |
| 774 | } |
| 775 | |
| 776 | // Overflow-scroll area. |
| 777 | if (auto* scrollableArea = frameView->scrollableAreaForScrollLayerID(scrollingNodeID)) { |
| 778 | scrollableArea->setCurrentHorizontalSnapPointIndex(horizontalIndex); |
| 779 | scrollableArea->setCurrentVerticalSnapPointIndex(verticalIndex); |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | void AsyncScrollingCoordinator::deferTestsForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) const |
| 784 | { |
| 785 | ASSERT(isMainThread()); |
| 786 | if (!m_page || !m_page->expectsWheelEventTriggers()) |
| 787 | return; |
| 788 | |
| 789 | if (const auto& trigger = m_page->testTrigger()) { |
| 790 | LOG(WheelEventTestTriggers, " (!) AsyncScrollingCoordinator::deferTestsForReason: Deferring %p for reason %d." , identifier, reason); |
| 791 | trigger->deferTestsForReason(identifier, reason); |
| 792 | } |
| 793 | } |
| 794 | |
| 795 | void AsyncScrollingCoordinator::removeTestDeferralForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) const |
| 796 | { |
| 797 | ASSERT(isMainThread()); |
| 798 | if (!m_page || !m_page->expectsWheelEventTriggers()) |
| 799 | return; |
| 800 | |
| 801 | if (const auto& trigger = m_page->testTrigger()) { |
| 802 | LOG(WheelEventTestTriggers, " (!) AsyncScrollingCoordinator::removeTestDeferralForReason: Deferring %p for reason %d." , identifier, reason); |
| 803 | trigger->removeTestDeferralForReason(identifier, reason); |
| 804 | } |
| 805 | } |
| 806 | #endif |
| 807 | |
| 808 | #if ENABLE(CSS_SCROLL_SNAP) |
| 809 | bool AsyncScrollingCoordinator::isScrollSnapInProgress() const |
| 810 | { |
| 811 | return scrollingTree()->isScrollSnapInProgress(); |
| 812 | } |
| 813 | |
| 814 | void AsyncScrollingCoordinator::updateScrollSnapPropertiesWithFrameView(const FrameView& frameView) |
| 815 | { |
| 816 | if (auto node = downcast<ScrollingStateFrameScrollingNode>(m_scrollingStateTree->stateNodeForID(frameView.scrollingNodeID()))) { |
| 817 | setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Horizontal, frameView.horizontalSnapOffsets(), frameView.horizontalSnapOffsetRanges(), m_page->deviceScaleFactor()); |
| 818 | setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Vertical, frameView.verticalSnapOffsets(), frameView.verticalSnapOffsetRanges(), m_page->deviceScaleFactor()); |
| 819 | node->setCurrentHorizontalSnapPointIndex(frameView.currentHorizontalSnapPointIndex()); |
| 820 | node->setCurrentVerticalSnapPointIndex(frameView.currentVerticalSnapPointIndex()); |
| 821 | } |
| 822 | } |
| 823 | #endif |
| 824 | |
| 825 | void AsyncScrollingCoordinator::reportExposedUnfilledArea(MonotonicTime timestamp, unsigned unfilledArea) |
| 826 | { |
| 827 | if (m_page && m_page->performanceLoggingClient()) |
| 828 | m_page->performanceLoggingClient()->logScrollingEvent(PerformanceLoggingClient::ScrollingEvent::ExposedTilelessArea, timestamp, unfilledArea); |
| 829 | } |
| 830 | |
| 831 | void AsyncScrollingCoordinator::reportSynchronousScrollingReasonsChanged(MonotonicTime timestamp, SynchronousScrollingReasons reasons) |
| 832 | { |
| 833 | if (m_page && m_page->performanceLoggingClient()) |
| 834 | m_page->performanceLoggingClient()->logScrollingEvent(PerformanceLoggingClient::ScrollingEvent::SwitchedScrollingMode, timestamp, reasons); |
| 835 | } |
| 836 | |
| 837 | } // namespace WebCore |
| 838 | |
| 839 | #endif // ENABLE(ASYNC_SCROLLING) |
| 840 | |