Assertion failure: asr == nullptr painting a top-layer popover anchored inside a position: sticky subtree
Categories
(Core :: Layout: Positioned, defect)
Tracking
()
People
(Reporter: dao, Unassigned)
References
Details
(Keywords: assertion, testcase)
Attachments
(1 file)
|
854 bytes,
text/html
|
Details |
Painting a top-layer popover that is anchor-positioned to an element inside a position: sticky subtree hits
Assertion failure: asr == nullptr, at layout/generic/nsIFrame.cpp:4595
and takes the content process with it, via ViewportFrame::BuildDisplayListForContentTopLayer -> BuildDisplayListForTopLayerFrame -> nsIFrame::BuildDisplayListForChild. Debug only; release builds paint the popover correctly.
Steps to reproduce
Load the attached minimal.html in a debug build on a real widget backend. Headless never reproduces it, having no APZ:
xvfb-run -a -s "-screen 0 1600x1200x24" env -u WAYLAND_DISPLAY MOZ_ENABLE_WAYLAND=0 \
GDK_BACKEND=x11 XPCOM_DEBUG_BREAK=abort \
./mach run --temp-profile /path/to/minimal.html
That is the whole test case, minus a script that calls showPopover():
<div style="position:sticky">
<div style="anchor-name:--a">
<div popover style="position:absolute;height:110px;inline-size:1268px;
position-anchor:--a;position-area:span-block-end">
<span style="position:absolute;height:12px;inline-size:12px"></span>
</div>
</div>
</div>
It asserts on 3 runs out of 3.
Ingredients
Reduced from an about:newtab page by delta debugging. Each item below was verified by removing it and confirming the page still runs and still opens the popover, but no longer asserts.
position: stickyon an ancestor of the anchor. Static or relative is clean, and so is moving the anchor out of the sticky subtree. The sticky element does not have to be stuck, and the page does not have to be scrollable.anchor-nameplusposition-anchor, andposition-area.- The
popoverattribute. The same shape outside the top layer is clean. position: absoluteon the popover. Without it the popover is fixed and hits a different, intermittent assertion instead,PresContext()->Document()->GetActiveViewTransition()a few lines below.- A non-trivial
heightandinline-sizeon the popover. - An absolutely-positioned descendant inside the popover. Making that child
position: relativeis clean.
Guess at the cause
Offered as a hypothesis from reading the code, not something I confirmed in a debugger. The assertion sits on the branch that handles a mismatch between savedOutOfFlowData->mContainingBlockActiveScrolledRoot and DisplayPortUtils::GetASRAncestorFrame(child->GetParent()), and the asserts on that branch encode the assumption that such a mismatch can only be the view-transition native-anonymous-content case from bug 2002160. Anchor positioning looks like a second way to reach a legitimate mismatch: GetASRAncestorFrame follows the anchor chain through AnchorPositioningUtils::GetAnchorThatFrameScrollsWith, so an anchored out-of-flow takes its ASR from its anchor rather than from its parent. That would also account for the absolutely-positioned descendant being required, it being the child whose containing block is the anchored popover.
No CSS-only mitigation found
All of these still assert: anchor-name on the sticky element itself rather than on a descendant of it; contain: paint, contain: layout, will-change: transform, anchor-scope or inset-block-start: 0 on the sticky element; contain: layout, contain: paint, overflow: hidden or transform: translateZ(0) on the popover; and wrapping the popover's absolutely-positioned child in a position: relative div.
How this was hit
Putting the address bar on about:newtab (bug 2062215) anchors its results view to the search input, which sits inside newtab's sticky search wrapper. That fails browser/components/urlbar/tests/browser/browser_newtab_result_icon.js on every debug platform, so the patch cannot land with the CSS-only anchoring.
Build: local debug build of autoland f9a2ea974170 plus front-end-only patches that this file:// test case does not load.
| Reporter | ||
Comment 1•9 days ago
|
||
Description
•