Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: 0a314be5ad51d9d6cae901fa0aea25a4a1e8eed3
      
https://github.com/WebKit/WebKit/commit/0a314be5ad51d9d6cae901fa0aea25a4a1e8eed3
  Author: Matt Woodrow <[email protected]>
  Date:   2026-10-03 (Sat, 03 Oct 2026)

  Changed paths:
    M Source/WebKit/GPUProcess/GPUConnectionToWebProcess.cpp
    M Source/WebKit/GPUProcess/GPUConnectionToWebProcess.h
    M Source/WebKit/GPUProcess/GPUConnectionToWebProcess.messages.in
    M Source/WebKit/GPUProcess/GPUProcess.cpp
    M Source/WebKit/GPUProcess/GPUProcess.h
    M Source/WebKit/GPUProcess/GPUProcess.messages.in
    M Source/WebKit/GPUProcess/graphics/RemoteRenderingBackend.cpp
    M Source/WebKit/GPUProcess/graphics/RemoteRenderingBackend.h
    M Source/WebKit/GPUProcess/graphics/RemoteRenderingBackend.messages.in
    M Source/WebKit/GPUProcess/graphics/RemoteSnapshot.cpp
    M Source/WebKit/GPUProcess/graphics/RemoteSnapshot.h
    M Source/WebKit/GPUProcess/graphics/RemoteSnapshotIdentifier.h
    M Source/WebKit/Scripts/webkit/messages.py
    M Source/WebKit/Scripts/webkit/tests/MessageArgumentDescriptions.cpp
    M Source/WebKit/Shared/WTFArgumentCoders.serialization.in
    M Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivateForTesting.h
    M Source/WebKit/UIProcess/API/Cocoa/WKWebViewTesting.mm
    M Source/WebKit/UIProcess/Cocoa/GPUProcessProxyCocoa.mm
    M Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp
    M Source/WebKit/UIProcess/GPU/GPUProcessProxy.h
    A Source/WebKit/UIProcess/PendingSnapshotDrawing.h
    M Source/WebKit/UIProcess/WebPageProxy.cpp
    M Source/WebKit/UIProcess/WebPageProxy.h
    M Source/WebKit/UIProcess/WebPageProxy.messages.in
    M Source/WebKit/UIProcess/WebProcessProxy.cpp
    M Source/WebKit/UIProcess/WebProcessProxy.h
    M Source/WebKit/UIProcess/WebProcessProxy.messages.in
    M Source/WebKit/UIProcess/ios/WKContentView.mm
    M Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm
    M Source/WebKit/WebKit.xcodeproj/project.pbxproj
    M Source/WebKit/WebProcess/GPU/graphics/RemoteRenderingBackendProxy.cpp
    M Source/WebKit/WebProcess/GPU/graphics/RemoteRenderingBackendProxy.h
    M Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm
    M Source/WebKit/WebProcess/WebPage/WebPage.cpp
    M Source/WebKit/WebProcess/WebPage/WebPage.h
    M Source/WebKit/WebProcess/WebPage/WebPage.messages.in
    M Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm
    M Source/WebKit/WebProcess/WebProcess.cpp
    M Source/WebKit/WebProcess/WebProcess.h
    M Source/WebKit/WebProcess/WebProcess.messages.in
    M Tools/TestWebKitAPI/Tests/WebKit/WKWebView/SiteIsolation.mm

  Log Message:
  -----------
  [Site Isolation] Printing on the main thread deadlocks once a cross-site 
iframe has to be drawn
https://bugs.webkit.org/show_bug.cgi?id=325772
<rdar://problem/188867397>

Reviewed by Kimmo Kinnunen.

When UIKit prints on the main thread, WKContentView blocks until the document
has been drawn. With remote snapshotting, a frame hosted in another process is
asked to record by way of the UI process: the root sends DrawFrameToSnapshot,
the UI process forwards it, and the root only replied once every such frame
had replied back through the UI process. A blocked UI process dispatches none
of that, so it and the root waited on each other forever. The wait was also
for the wrong reply (DrawToPDFiOS rather than DrawPrintingPagesToSnapshotiOS,
and DrawRectToImage rather than DrawPrintingToSnapshotiOS), on the main frame's
process rather than the printed frame's, and the document itself comes back
from the GPU process, which nothing waited for.

Make the GPU process the one that decides when a snapshot is complete, rather
than the chain of replies. A snapshot's root is unresolved from the moment the
snapshot exists, and a frame's placeholders are recorded before its own display
list is sunk, so the snapshot is complete when no placeholder is left
unresolved. The sinks wait for that instead of requiring it. The root no longer
waits for the frames it asked for, so its reply only means its own frame is in,
and DrawFrameToSnapshot has no reply. The root also tells the GPU process the
size it was recorded at, which is what the snapshot is drawn at, so the UI
process asks for it to be drawn as soon as it has asked for the root, without
waiting for the root's reply.

That leaves routing as the only thing the UI process does for a frame, and
DrawFrameToSnapshot is dispatched even while it is waiting for a sync reply, and
in the frame's process even while that is waiting on the UI process, as it is
for alert(). Both hops of it are addressed to a process rather than a page: a
frame that goes away while it is being snapshotted can take the last of its
process's frames in the page with it, and with them the page a request was
addressed to, on either side. The UI process checks instead that the request
comes from the process hosting the frame's parent. A caller that must block now
does so in a sync wait on the GPU process (WaitForSnapshot), which is what
dispatches those requests, from any web process, including ones only found by
painting another frame. It is sent behind the sink, keeping its order, so the
sink's reply has come in by the time it returns. PendingSnapshotDrawing holds
what such a caller needs to wait for.

With no replies to report failure, a frame that will not be recorded is
abandoned in the GPU process instead, and draws nothing. The frame's process
does that if it cannot record it, over its connection to the GPU process since
there may be no page left, and the UI process does if it cannot route it,
including when the frame's parent has moved to another process since it was
painted. A root that cannot be recorded fails the snapshot the same way, since
a blocked UI process would not hear of it. None of that can happen once the
process has gone, so the UI process tells the GPU process which process owes
each frame, and the frame is abandoned when that process's connection to the
GPU process closes, or when the UI process says the process has gone, which
covers one that never connected and cannot overtake the ownership it reported.
Anything still missing after a deadline is abandoned too, so that a frame whose
process is merely busy cannot hold up the snapshot indefinitely.

Since frames are no longer waited for before the snapshot is sunk, one can now
come in after it was released. A snapshot used to be created by whichever of
its recorders reached the GPU process first, which would bring it back and
leak it. Now it is only created by its root, synchronously before painting, or
by the request to draw it if that comes first, neither of which can come after
it is released, and a recorder for one that does not exist records into
nothing. Every root goes through WebPage::recordRemoteSnapshot() for that.

Every process taking part in a snapshot acts on it by naming it: to record
into it, to abandon its frames, or to fail it. Its identifier was a counter,
which any web process could guess, so it is now a cryptographically random
UUID. Frame identifiers are still guessable, as the remaining FIXMEs say.

Test: SiteIsolation.DrawPagesToPDFSynchronouslyIncludesCrossSiteFrames. This
blocks the main thread the way UIKit printing does, through a new testing SPI,
so it covers the deadlock on macOS. Without DrawFrameToSnapshot being
dispatched during the wait, it times out and gets no document.

* Source/WebKit/GPUProcess/GPUConnectionToWebProcess.cpp:
(WebKit::GPUConnectionToWebProcess::abandonSnapshotFrame):
(WebKit::GPUConnectionToWebProcess::failSnapshot):
* Source/WebKit/GPUProcess/GPUConnectionToWebProcess.h:
* Source/WebKit/GPUProcess/GPUConnectionToWebProcess.messages.in:
* Source/WebKit/GPUProcess/GPUProcess.cpp:
(WebKit::GPUProcess::removeGPUConnectionToWebProcess):
(WebKit::GPUProcess::ensureSnapshot):
(WebKit::GPUProcess::snapshotDeadlineExpired):
(WebKit::GPUProcess::createSnapshot):
(WebKit::GPUProcess::failSnapshot):
(WebKit::GPUProcess::snapshotForRecorder): Replaces getOrCreateSnapshot(); no 
longer creates.
(WebKit::GPUProcess::snapshot):
(WebKit::GPUProcess::abandonSnapshotFrame):
(WebKit::GPUProcess::snapshotFrameWillBeDrawnByProcess):
(WebKit::GPUProcess::abandonSnapshotFramesOwnedBy):
(WebKit::GPUProcess::takeSnapshotWhenComplete):
(WebKit::GPUProcess::sinkCompletedSnapshotToPDF):
(WebKit::GPUProcess::sinkCompletedSnapshotToBitmap):
(WebKit::GPUProcess::releaseSnapshot):
(WebKit::GPUProcess::waitForSnapshot):
* Source/WebKit/GPUProcess/GPUProcess.h:
* Source/WebKit/GPUProcess/GPUProcess.messages.in:
* Source/WebKit/GPUProcess/graphics/RemoteRenderingBackend.cpp:
(WebKit::RemoteRenderingBackend::createSnapshot):
(WebKit::RemoteRenderingBackend::createSnapshotRecorder):
(WebKit::RemoteRenderingBackend::sinkSnapshotRecorderIntoSnapshotFrame):
* Source/WebKit/GPUProcess/graphics/RemoteRenderingBackend.h:
* Source/WebKit/GPUProcess/graphics/RemoteRenderingBackend.messages.in:
* Source/WebKit/GPUProcess/graphics/RemoteSnapshot.cpp:
(WebKit::RemoteSnapshot::create):
(WebKit::RemoteSnapshot::RemoteSnapshot):
(WebKit::RemoteSnapshot::addFrameReference):
(WebKit::RemoteSnapshot::resolveFrameWithLockHeld):
(WebKit::RemoteSnapshot::setFrame):
(WebKit::RemoteSnapshot::abandonFrame):
(WebKit::RemoteSnapshot::setFrameOwner):
(WebKit::RemoteSnapshot::abandonFramesOwnedBy):
(WebKit::RemoteSnapshot::abandonUnresolvedFrames):
(WebKit::RemoteSnapshot::size const):
(WebKit::RemoteSnapshot::setSize):
(WebKit::RemoteSnapshot::hasFailed const):
(WebKit::RemoteSnapshot::hasFailedWithLockHeld const):
(WebKit::RemoteSnapshot::fail):
(WebKit::RemoteSnapshot::isComplete const):
(WebKit::RemoteSnapshot::whenComplete):
(WebKit::RemoteSnapshot::isAwaited const):
(WebKit::RemoteSnapshot::dispatchCompletionHandlersIfComplete):
(WebKit::RemoteSnapshot::applyFrame const):
* Source/WebKit/GPUProcess/graphics/RemoteSnapshot.h:
* Source/WebKit/GPUProcess/graphics/RemoteSnapshotIdentifier.h: Now a UUID.
(WebKit::generateRemoteSnapshotIdentifier):
* Source/WebKit/Scripts/webkit/messages.py:
* Source/WebKit/Scripts/webkit/tests/MessageArgumentDescriptions.cpp:
* Source/WebKit/Shared/WTFArgumentCoders.serialization.in:
* Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivateForTesting.h:
* Source/WebKit/UIProcess/API/Cocoa/WKWebViewTesting.mm:
(-[WKWebView _drawPagesToPDFSynchronouslyForTesting:]):
* Source/WebKit/UIProcess/Cocoa/GPUProcessProxyCocoa.mm:
(WebKit::GPUProcessProxy::sinkCompletedSnapshotToPDF):
* Source/WebKit/UIProcess/GPU/GPUProcessProxy.cpp:
(WebKit::GPUProcessProxy::sinkCompletedSnapshotToBitmap):
(WebKit::GPUProcessProxy::abandonSnapshotFrame):
(WebKit::GPUProcessProxy::snapshotFrameWillBeDrawnByProcess):
(WebKit::GPUProcessProxy::abandonSnapshotFramesOwnedBy):
(WebKit::GPUProcessProxy::releaseSnapshotIfRootFails):
(WebKit::GPUProcessProxy::waitForSnapshot):
* Source/WebKit/UIProcess/GPU/GPUProcessProxy.h:
* Source/WebKit/UIProcess/PendingSnapshotDrawing.h: Added.
* Source/WebKit/UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::drawRectToImage):
(WebKit::WebPageProxy::drawPagesToPDF):
(WebKit::WebPageProxy::drawToPDF):
(WebKit::WebPageProxy::takeSnapshot):
(WebKit::WebPageProxy::drawFrameToSnapshot): Moved to WebProcessProxy.
* Source/WebKit/UIProcess/WebPageProxy.h:
* Source/WebKit/UIProcess/WebPageProxy.messages.in:
* Source/WebKit/UIProcess/WebProcessProxy.cpp:
(WebKit::WebProcessProxy::shutDown):
(WebKit::WebProcessProxy::drawFrameToSnapshot):
* Source/WebKit/UIProcess/WebProcessProxy.h:
* Source/WebKit/UIProcess/WebProcessProxy.messages.in:
* Source/WebKit/UIProcess/ios/WKContentView.mm:
(-[WKContentView _waitForDrawToPDFCallbackForPrintFormatterIfNeeded:]):
(-[WKContentView _waitForDrawToImageCallbackForPrintFormatterIfNeeded:]):
* Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::drawToPDFiOS):
(WebKit::WebPageProxy::drawToImage):
* Source/WebKit/WebKit.xcodeproj/project.pbxproj:
* Source/WebKit/WebProcess/GPU/graphics/RemoteRenderingBackendProxy.cpp:
(WebKit::RemoteRenderingBackendProxy::createSnapshot):
* Source/WebKit/WebProcess/GPU/graphics/RemoteRenderingBackendProxy.h:
* Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm:
(WebKit::WebPage::drawPrintingRectToSnapshot):
(WebKit::WebPage::drawPrintingPagesToSnapshot):
* Source/WebKit/WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::takeRemoteSnapshot):
(WebKit::WebPage::paintRemoteFrameContents):
(WebKit::WebPage::recordRemoteSnapshot):
(WebKit::WebPage::failRemoteSnapshotIfRootFails):
(WebKit::WebPage::drawToSnapshot):
(WebKit::WebPage::drawFrameToSnapshot):
* Source/WebKit/WebProcess/WebPage/WebPage.h:
* Source/WebKit/WebProcess/WebPage/WebPage.messages.in:
* Source/WebKit/WebProcess/WebProcess.cpp:
(WebKit::WebProcess::drawFrameToSnapshot):
(WebKit::WebProcess::abandonSnapshotFrame):
(WebKit::WebProcess::failSnapshot):
* Source/WebKit/WebProcess/WebProcess.h:
* Source/WebKit/WebProcess/WebProcess.messages.in:
* Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::drawPrintingToSnapshotiOS):
(WebKit::WebPage::drawPrintingPagesToSnapshotiOS):
* Tools/TestWebKitAPI/Tests/WebKit/WKWebView/SiteIsolation.mm:
(TestWebKitAPI::TEST(SiteIsolation, 
DrawPagesToPDFSynchronouslyIncludesCrossSiteFrames)):

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



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

Reply via email to