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
54namespace WebCore {
55
56AsyncScrollingCoordinator::AsyncScrollingCoordinator(Page* page)
57 : ScrollingCoordinator(page)
58 , m_updateNodeScrollPositionTimer(*this, &AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScrollTimerFired)
59 , m_scrollingStateTree(std::make_unique<ScrollingStateTree>(this))
60{
61}
62
63AsyncScrollingCoordinator::~AsyncScrollingCoordinator() = default;
64
65void AsyncScrollingCoordinator::scrollingStateTreePropertiesChanged()
66{
67 scheduleTreeStateCommit();
68}
69
70#if ENABLE(CSS_SCROLL_SNAP)
71static 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
97void 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
104void AsyncScrollingCoordinator::willCommitTree()
105{
106 updateEventTrackingRegions();
107}
108
109void 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
121void 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
152void 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
165void AsyncScrollingCoordinator::frameViewEventTrackingRegionsChanged(FrameView& frameView)
166{
167 if (!m_scrollingStateTree->rootStateNode())
168 return;
169
170 setEventTrackingRegionsDirty();
171 DebugPageOverlays::didChangeEventHandlers(frameView.frame());
172}
173
174void 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
207bool 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
241void AsyncScrollingCoordinator::applyScrollingTreeLayerPositions()
242{
243 m_scrollingTree->applyLayerPositions();
244}
245
246void 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
268void AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScrollTimerFired()
269{
270 updateScrollPositionAfterAsyncScroll(m_scheduledScrollUpdate.nodeID, m_scheduledScrollUpdate.scrollPosition, m_scheduledScrollUpdate.layoutViewportOrigin, ScrollType::User, m_scheduledScrollUpdate.updateLayerPositionAction);
271}
272
273FrameView* 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
305void 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(ScrollableArea::scrollOffsetFromPosition(scrollPosition, toFloatSize(scrollableArea->scrollOrigin())));
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
354void 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* headerLayer = headerLayerForFrameView(frameView);
399 auto* footerLayer = 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 positionForHeaderLayer = FloatPoint(scrollPositionForFixed.x(), FrameView::yPositionForHeaderLayer(scrollPosition, topContentInset));
411 FloatPoint positionForFooterLayer = 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
447void 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
469void 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
489ScrollingNodeID 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
495ScrollingNodeID 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
501void AsyncScrollingCoordinator::unparentNode(ScrollingNodeID nodeID)
502{
503 m_scrollingStateTree->unparentNode(nodeID);
504}
505
506void AsyncScrollingCoordinator::unparentChildrenAndDestroyNode(ScrollingNodeID nodeID)
507{
508 m_scrollingStateTree->unparentChildrenAndDestroyNode(nodeID);
509}
510
511void AsyncScrollingCoordinator::detachAndDestroySubtree(ScrollingNodeID nodeID)
512{
513 m_scrollingStateTree->detachAndDestroySubtree(nodeID);
514}
515
516void AsyncScrollingCoordinator::clearAllNodes()
517{
518 m_scrollingStateTree->clear();
519}
520
521ScrollingNodeID 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
530Vector<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
548void 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
555void 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
567void 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
592void 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
611void 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
634void 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
675void 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
695void 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
706void 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
717void 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
731bool AsyncScrollingCoordinator::isRubberBandInProgress() const
732{
733 return scrollingTree()->isRubberBandInProgress();
734}
735
736void AsyncScrollingCoordinator::setScrollPinningBehavior(ScrollPinningBehavior pinning)
737{
738 scrollingTree()->setScrollPinningBehavior(pinning);
739}
740
741bool AsyncScrollingCoordinator::asyncFrameOrOverflowScrollingEnabled() const
742{
743 auto& settings = m_page->mainFrame().settings();
744 return settings.asyncFrameScrollingEnabled() || settings.asyncOverflowScrollingEnabled();
745}
746
747String 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)
759void 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
783void 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
795void 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)
809bool AsyncScrollingCoordinator::isScrollSnapInProgress() const
810{
811 return scrollingTree()->isScrollSnapInProgress();
812}
813
814void 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
825void 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
831void 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