[LBSE] Preserve paint order of non-composited children around composited SVG siblings
LBSE(Layer-Based SVG Engine)는 레거시 단일 backing store SVG 렌더러와 달리, SVG 요소를 HTML과 유사한 방식으로 RenderLayer에 매핑합니다. Compositor는 컨테이너 자식들로 구성된 GraphicsLayer 형제 목록을 조립하는데, composited 자식(will-change, 3D transform, opacity 승격)은 독립적인 GraphicsLayer가 되고 non-composited 자식은 backing store를 공유합니다. Compositor는 목록 순서대로 layer를 쌓기 때문에, non-composited backing 하나와 여러 composited 형제가 섞인 컨테이너에서는 임의의 DOM paint order를 표현할 수 없습니다. 공유 layer는 모든 composited 형제의 완전히 뒤쪽 또는 완전히 앞쪽에 배치될 수밖에 없기 때문입니다.
LayoutTests/TestExpectations
Source/WebCore/rendering/RenderLayerBackingSVGAdditions.cpp
이 commit은 paint order segment overlay GraphicsLayer를 도입합니다. SVG의 flat child 목록은 각 "anchor"(composited child) 경계에서 [begin, end) 형태의 index range로 분할됩니다. 첫 번째 구간은 컨테이너의 backing store에서 직접 렌더링됩니다. 이후 각 non-composited 자식 구간에는 "(svg segment N)"이라는 이름의 GraphicsLayer가 할당되고, compositor의 child 목록에서 직전 anchor 바로 뒤에 삽입됩니다. 한편 "derived anchor"는 자손이 composited이기 때문에 anchor가 되는 요소로, 인라인으로 paint됩니다. 이런 요소가 있으면 경계 감지가 복잡해지는데, derived anchor 앞의 빈 leading 구간에서도 반드시 segment를 생성해야 하기 때문입니다. RenderSVGForeignObject는 SVG DOM 순서 paint path가 아닌 HTML 방식의 z-order 목록으로 paint되기 때문에 segmentation 대상에서 제외됩니다.
Flat child list: [r1, a1(composited), r2, r3, a2(composited), r4]
Before (broken): After (correct):
container-primary → r1,r2,r3,r4 container-primary → slice [0,1) → r1
a1 GraphicsLayer a1 GraphicsLayer → a1's content
a2 GraphicsLayer (svg segment 1) → slice [2,4) → r2,r3
a2 GraphicsLayer → a2's content
(svg segment 2) → slice [4,6) → r4
Significance
이 commit은 새로운 LBSE compositing pipeline에서 발생하던 근본적인 z-order 정확성 위반을 수정합니다. 애니메이션 또는 transform이 적용된 요소의 non-composited SVG 형제 요소가 잘못된 순서로 렌더링되던 문제가 해결되었습니다. 아울러 복잡한 lifetime 관리, index 연산, 동적 업데이트 로직을 포함하는 상당한 규모의 새로운 compositing 서브시스템이 도입되었으며, 이는 새로운 attack surface로 작용합니다.
Audit directions
updateSVGSegmentLayers에서 계산되어 paint method에서 사용되는 flat child list 전체의 [begin, end) index range 연산은, off-by-one이 발생하기 쉬운 전형적인 영역입니다. Anchor 삽입·제거·재배치 시 index가 원자적으로 갱신되는지, 그리고 어떤 자식도 두 개의 중첩된 segment에 의해 중복 렌더링되지 않는지 확인해야 합니다. Derived anchor 처리는 미묘합니다. Compositing traversal과 paint 사이에 "derived" 판정이 달라지면, segment가 빈 range 또는 중복된 range를 생성할 가능성이 있습니다. 동적 DOM 변경이 발생할 때마다 overlay GraphicsLayer 목록이 매번 재구성됩니다. 저장된 segment 목록과 현재 compositor child 목록 간의 매칭 로직은, 소멸과 진행 중인 paint 사이에 race condition이 발생하면 stale-layer UAF로 이어질 가능성이 있습니다. Transform이 적용된 컨테이너 경로는 slice 경계로 자르지 않고 foreground 크기 전체의 backing을 유지합니다. 이는 의도적으로 연기된 제한 사항으로, 조건 분기에 따라 size heuristic이 달라지는 지점은 살펴볼 필요가 있습니다. 마지막으로, RenderSVGForeignObject를 일반 SVG 자식으로 잘못 분류하는 경로가 있다면 segment index range가 손상될 가능성이 있습니다.