Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: 80eaeca1f565e58a3df57b5d6b2a77cb40446dea
      
https://github.com/WebKit/WebKit/commit/80eaeca1f565e58a3df57b5d6b2a77cb40446dea
  Author: Simon Fraser <[email protected]>
  Date:   2026-04-28 (Tue, 28 Apr 2026)

  Changed paths:
    M Source/WebCore/PAL/pal/spi/cocoa/QuartzCoreSPI.h
    M Source/WebKit/Configurations/AllowedSPI.toml
    M 
Source/WebKit/UIProcess/RemoteLayerTree/mac/RemoteLayerTreeEventDispatcher.h
    M 
Source/WebKit/UIProcess/RemoteLayerTree/mac/RemoteLayerTreeEventDispatcher.mm
    M Source/WebKit/UIProcess/RemoteLayerTree/mac/RemoteScrollingTreeMac.mm

  Log Message:
  -----------
  Occasional flicker of bad layer offsets when positions are adjusted by scroll 
anchoring (or other programmatic scrolling)
https://bugs.webkit.org/show_bug.cgi?id=313486
rdar://175706332

Reviewed by Abrar Rahman Protyasha.

On macOS, we process wheel events in the scrolling thread, which can happen 
concurrently
with the main thread receiving a 
RemoteLayerTreeTransaction/RemoteScrollingCoordinatorTransaction.
If the web process issued a programmatic scroll (e.g. for a scroll anchoring 
adjustment),
the layer tree transaction may contain new layer backing stores with the newly 
rendered
scroll offset, and the scrolling transaction will have nodes with 
RequestedScrollPositions.
To avoid flashes of incorrect content, it's essential that the new positions 
and backing
stores show up in the same Core Animation commit.

However there was no guarantee of that. When the main thread receives the 
transaction
with RequestedScrollPositions, the scrolling thread may have recently processed 
wheel
events, and have pending layer position updates. The main thread may then 
process the
RequestedScrollPositions, which modify ScrollingTreeNode 
`currentScrollPosition`, which
affects layer positions. Now, if the scrolling thread's CACommit happens before 
the
main thread, the scrolling thread commits positions affected by programmatic 
scrolls,
but the layer backing stores still show the old state; this is the flash of bad 
state.

Reduce the window of opportunity here by changing the scrolling thread to use 
explict
CATransaction flushing, rather than relying on automatic runloop observer-based 
commits,
which was the main cause of raceyness. We can't just use explict 
`begin`/`commit` pairs
because there are code paths that touch scrollbar layers that open implicit 
transactions;
those still need to get flushed.

We call `[CATransaction flush]` for any `ScrollingThread::dispatch()` that can
touch layers; the most common is the case that updates layers and animations,
and has to flush before updating animations (which uses 
CAPresentationModifierGroup).

Now that we have explicit flushing, we can, if necessary, use it to eliminate 
the
race in future.

* Source/WebCore/PAL/pal/spi/cocoa/QuartzCoreSPI.h:
* Source/WebKit/Configurations/AllowedSPI.toml: Just whitespace.
* Source/WebKit/UIProcess/RemoteLayerTree/mac/RemoteLayerTreeEventDispatcher.h:
* Source/WebKit/UIProcess/RemoteLayerTree/mac/RemoteLayerTreeEventDispatcher.mm:
(WebKit::RemoteLayerTreeEventDispatcher::scrollingThreadHandleWheelEvent):
(WebKit::RemoteLayerTreeEventDispatcher::wheelEventHandlingCompleted):
(WebKit::RemoteLayerTreeEventDispatcher::didRefreshDisplay): 
tryToApplyLayerPositions() doesn't
have to run with the lock held.
(WebKit::RemoteLayerTreeEventDispatcher::updateLayerPositionsAndAnimations):
(WebKit::RemoteLayerTreeEventDispatcher::delayedRenderingUpdateDetectionTimerFired):
(WebKit::RemoteLayerTreeEventDispatcher::waitForRenderingUpdateCompletionOrTimeout):
(WebKit::RemoteLayerTreeEventDispatcher::timeline):
* Source/WebKit/UIProcess/RemoteLayerTree/mac/RemoteScrollingTreeMac.mm:
(WebKit::RemoteScrollingTreeMac::RemoteScrollingTreeMac):

Canonical link: https://commits.webkit.org/312233@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications

Reply via email to