← All reports

[HTML-in-Canvas] Drawing the canvas by setting the content of the layer

Component: WebCore Canvas | 1b9d7b2

Source/WebKit/GPUProcess/graphics/RemoteImageBuffer.messages.in

+ PrepareForDisplay() -> (std::optional<WebKit::ImageBufferBackendHandle> backendHandle)

Source/WebCore/platform/graphics/ImageBufferBackend.h

+ virtual RefPtr<GraphicsLayerContentsDisplayDelegate> layerContentsDisplayDelegate();
+ virtual void releaseLayerContentsDisplayDelegate();
- virtual RefPtr<GraphicsLayerContentsDisplayDelegate> layerContentsDisplayDelegate() const;

Source/WebCore/rendering/RenderLayerBacking.cpp

(WebCore::RenderLayerBacking::containsPaintedContent const)
(WebCore::RenderLayerBacking::destroyGraphicsLayers)

Canvas가 화면에 표시되는 방식은 두 가지입니다. 먼저 layer의 backing store에 직접 paint하는 방식이 있습니다. 반대로 accelerated canvas는 display delegate를 기반으로 하는 contents layer를 compositor에 전달하므로, repaint가 필요하지 않습니다. 이때 display delegate는 Cocoa에서는 IOSurface, GTK/WPE에서는 Skia 기반 surface에 해당합니다. GPU process 모드에서는 ImageBuffer가 GPU process에 존재하고, WebContent는 RemoteImageBufferProxy만 보유하게 됩니다. 그래서 화면에 표시할 surface를 IPC로 조회해야 합니다. 한편 HTML-in-Canvas는 "drawable" 자식 DOM element를 canvas 안에 렌더링하는 기능입니다. 이를 지원하려면 layout과 paint가 끝난 뒤에도 canvas 내용이 바뀔 수 있어야 합니다.

이번 commit으로 accelerated backend를 사용하는 drawable 2D canvas의 표시 방식이 변경되었습니다. 기존에는 GraphicsLayer에 paint했지만(CanvasPaintedToLayer), 이제는 contents layer(CanvasAsLayerContents)를 통해 표시됩니다. 이를 위해 ImageBufferBackend에는 non-const layerContentsDisplayDelegate()와 releaseLayerContentsDisplayDelegate()가 추가되었습니다. 또한 새로운 RemoteImageBuffer::PrepareForDisplay message가 도입되어, display surface를 WebContent로 반환합니다.

WebContent                               GPU Process
RemoteImageBufferProxy
  ├── PrepareForDisplay ───────────────► RemoteImageBuffer
  │                                        └─► backend.prepareForDisplay()
  │◄── display surface ────────────────────┘
  └─► layerContentsDisplayDelegate() ─► contents layer (CanvasAsLayerContents)
disconnect() / releaseLayerContentsDisplayDelegate() ─► drop delegate

이 변경으로 canvas pixel이 Paint 단계 이후에도 갱신될 수 있게 되었습니다. HTML-in-Canvas의 paint event를 Paint 이후에 발생시키기 위한 전제 조건인 셈입니다. WebContent에서 GPU process로 향하는 PrepareForDisplay message가 추가되었으며, 이 message는 display surface handle을 반환합니다.

Compromised WebContent는 임의의 시점에 PrepareForDisplay를 전송할 수 있습니다. 따라서 remote image buffer가 이미 해제된 상태, backend가 non-accelerated인 상태, 같은 message가 반복 전송되는 경우의 동작을 점검할 필요가 있습니다. 한편 display delegate는 이제 WebContent, GPU process, compositor 사이에서 공유되고, release/disconnect 경로도 새로 생겼습니다. 이 구조에서는 canvas가 drawable과 non-drawable 사이를 오가는 경우를 살펴봐야 합니다. 이때 painted 모드와 contents-layer 모드가 전환되면서 clearContentsFromLayer가 호출됩니다. 또한 GPU process crash 이후의 복구 과정에서, stale delegate나 surface가 해제 이후에 다시 사용되는(use-after-release) 경로가 없는지도 확인이 필요합니다.