Repository navigation
Implement position: sticky - #887
LinuxBoy-96 wants to merge 10 commits into
Conversation
|
Read through the whole patch — clean design, and several details most implementations get wrong are right here:
Two real bugs to check:
Minor notes:
|
|
@yinnho Thanks again for the feedback. I think I fixed what your saying, Well at least I did some testing it seem very fine, at least good enought for a merge. If it is I will be confident enought work on Shadow DOM and text-overflow this week. |
|
Re-reviewed b851c33 against Chrome 152 (headless):
<body style="margin:0">
<div style="height:50px"></div>
<div style="transform:translateZ(0)">
<div style="position:sticky;top:0;height:40px">sticky</div>
<div style="height:1200px"></div>
</div>
</body>
The rest (sticky_nodes caching, |
|
You are right, easy fix. |
|
Some performance concerns with this (this does a full tree walk every frame to find sticky nodes!)
Correctness:
|
|
Verified cb7ca14 against the Chrome probe from my last comment — the walk now stops at the nearest scroll container or On the caret/selection item: we hit the same read/write split when we made |
|
Read the new "Single geometry funnel" commit — this is the right consolidation. Verified all five refactored sites ( Two details worth calling out as Chrome-correct: keeping the rounded/unrounded split (paint/hit on Still holding my approval on the pair from the first review (the sticky walk not stopping at a |
|
Follow-up from our earlier review (we said we'd hold until the fixed-subtree and nested-scroller fixes landed): while waiting we shipped our own Scope first, to keep the numbers honest: ours is narrower than this PR in places (per-parent z-band, no boxless table rows yet). Three commits, ~1.6k insertions including tests, 1,348-test suite green, clippy count unchanged. v1, root scroller (+756): sticky lays out in-flow (taffy Relative, insets clamped to AUTO at the taffy boundary — same call this PR makes), and pins at read time via a shift map keyed by v2, element-level scrollers (+630): one unified depth-ascending walk computes per-node telescoping shifts (base + own + accumulated), so nested scrollers compose — this is bug #2 from our review, and we can confirm doing it as a single walk up front was much simpler than patching nearest-scroller-only afterwards. Fixed/transformed ancestors gate to zero shift (bug #1's fix shape). One trap this PR will hit too: our first pass ordered the flat paint list per-parent, so a stuck header with And +1 on nicoburns' ①/②: our shift map is exactly the "compute constraints once per layout, clamp per scroll" shape. Keying by epoch meant we never had to maintain a sticky node list — the cache entry just dies when layout changes. |
|
@yinnho Thanks for the aginxbrowser data point. The z-band trap is already covered |
|
Correction to my close-out comment — I inverted the transformed-ancestor case in the cb7ca14 bullet. The patch drops the transform stop ( |
|
Is this mergeable now, @nicoburns ? Keeping three PRs alive at once is starting to hurt :p |
Not yet! Remaining issues from AI review:
And a couple of nits:
Happy to post these as PR comments if you want.
Imagine how I feel trying to review 3 PRs at once (plus probably something like 20-100 PRs across Blitz and all of the other related repositories I maintain)! We're not a slop project, so everything needs to actually be reviewed (not just by AI - although I'm using AI review as a first line of defence to maintain my sanity) in order to maintain quality, and that takes a lot of time! Especially for LLM generated PRs Your PRs are actually for high-priority features which is getting them a lot more attention than they would otherwise. But they're still non-trivial implementations, and I'm guessing by the speed at which you are responding to the feedback, mostly LLM generated (?) (which means that I don't have your own judgement or explanation of why you've implemented things the way you have to lean on, and have to carefully verify everything myself from scratch). |
5c82865 to
62582a1
Compare
|
All three are fixed
Well I feel you a lot, you are rights, comments in the code are not from me, I never documents code myself.. I'm fast, but not supe-human fast, beside some works I did on these matter before , AI chose to guide me on that speciffic path too, does it's resoning is better than me alone? most likely.. Would I forgot something without AI? yes. So as long as the code don't look stupid, and pass my test, it's fine on my side. Take your time, I still have a lot of time too |
|
A couple more:
Probably those'll be the last ones before human review. |
|
Should be fine I hope. |
|
@nicoburns I see a new conflict... I will probably fix it this week-end, but I'm wondering if these PR have lost any kind on momentum they had... |
|
I'm currently sick, so don't have much capacity this week. My priority when I do is to land |
|
Absolute/fixed hoisting landed which unblocks this (the vertical align stuff still needs to land, but that shouldn't conflict) |
8cf8a4a to
6beade9
Compare
|
@nicoburns All conflits on 3 Prs are resolved. |
… expose them to getComputedStyle
|
I've pushed a small extra change which makes some more tests pass |
34fc0c5 to
e6c6832
Compare
Sticky boxes were laid out and painted as
position: relative. This adds themissing shift: after layout (and before every paint, which covers scrolling),
each sticky box is moved so that it stays within the inset rectangle of its
nearest scroll container's scrollport, clamped to its containing block.
How
stylo_taffy: a sticky box is still laid out asrelative, but its insetsare no longer handed to Taffy as relative offsets (they were: a
top: 50pxsticky box was permanently shifted by 50px). Insets are sticking thresholds,
applied by the sticky resolution below.
blitz-dom/src/sticky.rs(new):BaseDocument::update_sticky_offsets()walks the tree and, for each
position: stickybox, resolvestop/right/bottom/leftagainst the scrollport (nearest ancestor with ascrolling
overflow, else the viewport), computes the shift per axis andstores it in
LayoutData::sticky_offset. The pure functionaxis_offsetcarries the clamping logic and has unit tests.
LayoutData::sticky_offset(new field),Node::sticky_offset().blitz-paint/src/render.rs): the shift is added to the box position.node.rs::hit_inner): the shift is subtracted, so clicks landon the box where it is drawn.
unrounded_absolute_positionandoffset_top_leftadd it, sogetBoundingClientRectandoffsetTop/offsetLeftreport the drawn position;absolute_position(used by
scrollIntoView) keeps the in-flow position.(
write_scroll_offset,set_viewport_scroll), so scripts measuring rightafter
scrollTo()see the stuck position without waiting for a paint.resolve()refreshes offsets after layout;paint_scenerefreshes thembefore painting, so scroll changes are reflected without a relayout.
Covered
top/right/bottom/leftin any length or percentage (resolvedagainst the scrollport), against the viewport or the nearest scroll
container.
the border box.
the inner box's position.
than the space between
topandbottom(or wider thanleft/right)sits at the winning inset: block-start / inline-start, derived from the
containing block's
writing-modeanddirection(leftwins in LTR,rightin RTL,topin horizontal writing modes).scrollable area (
scrollWidth/scrollHeight), not the visible box.transform, keepingthe spec order for transformed sticky boxes and transformed ancestors.
trandthead/tbody/tfoothave no box of their own. A stickyth/tdisconstrained by the nearest boxed ancestor (the table); a sticky
tr/ rowgroup is measured from its cells and its shift is handed down to them for
painting and hit testing.
Notes / limitations
the spec requires; only its painted/hit position changes.
sticky box are not composed, matching current engine behaviour).
pages; can be replaced by a list of sticky nodes maintained at style
resolution if profiling shows it.
Results — WPT
css/css-position/sticky(116 tests)main: 29 passed (30.67 counting partials), 36/164 subtests.No regression.
(X)reftests, which need the runner to execute scripts(the
table-*reftests are among them);single-axis-*(newer specfeature);
transforms*(transformed-ancestor geometry in CSSOM);writing-modes(behind thelayout.writing-mode.enabledpref);get-bounding-client-rect2/3 (script insertion into an already scrolledscroller, fails on
maintoo).Tests
cargo test -p blitz-dom sticky(7 tests, one taken verbatim from the WPToverconstrained case)
WPT_DIR=... cargo run --release --package wpt -- css/css-position/stickyWPT results
Subtests: 212 newly passing, 2 newly failing (net +210). Crashes: +1.
Full diff (44 changed tests)
Generated by the WPT workflow.