LibWeb: Make formatting contexts size their own root boxes - #10971
Conversation
Root box sizing used to be the launching context's job: the block, inline, and abspos parents each computed a child's used sizes around its inner layout, the table context sized itself, and a two-phase finish callback plus a pre-created used-values whitelist papered over the places where that split leaked. Now sizes flow down as constraints and come back up as results. LayoutInput carries the containing block's available space and percentage bases, how the root participates in its parent's layout (block-level, float, atomic inline, out-of-flow, item, or entry root), and parent-authoritative directives such as forced viewport sizes and the float-avoidance inline space. Each formatting-context run sizes its own root in a prelude, lays out its contents against the root's inner space, resolves the automatic block size in an epilogue, and returns plain results the parent uses to place the box. The parent probes float avoidance with the same non-committing sizing function the prelude later commits, so measure and commit cannot diverge. The completion protocol, the whitelist, and the table-offset side channel are gone.
📝 WalkthroughWalkthroughThe PR refactors Rust layout execution from mutable ChangesRust layout execution
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Document
participant LayoutRustBridge
participant FormattingContextRun
participant BlockFormattingContext
participant AbsposEngine
Document->>LayoutRustBridge: start root layout with viewport dimensions
LayoutRustBridge->>FormattingContextRun: create run-based layout state
FormattingContextRun->>BlockFormattingContext: execute root formatting context
BlockFormattingContext->>FormattingContextRun: lay out child and return ChildLayoutResult
FormattingContextRun->>AbsposEngine: dispatch absolute-positioned descendants
AbsposEngine->>FormattingContextRun: replay positioned layout
Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs`:
- Around line 2340-2348: Align the float-root block-size resolution in
dimension_float_root with the block-level path by passing
self.sizing().available_space_for_block_size_resolution(...) instead of
input.available_space where resolving used block sizes. Preserve the existing
auto-size branching and ensure quirks-mode percentage height uses the same
containing-block basis as non-floating boxes.
In `@Libraries/LibWeb/Rust/src/layout/formatting_context.rs`:
- Around line 1379-1403: Update size_skipped_independent_root’s BlockLevel
branch to resolve automatic block size for skipped roots, matching the auto-size
handling in finalize_block_level_root() while retaining the existing
definite-size resolution performed by
resolve_block_level_root_block_size_before_body().
In `@Libraries/LibWeb/Rust/src/layout/sizing_context.rs`:
- Around line 1136-1155: Update the atomic inline root sizing flow around
dimension_atomic_root and finalize_atomic_root_block_size so block-axis
intrinsic constraints from input.available_space.block_size are preserved
through block-size resolution. Do not replace the original block available space
with AvailableSize::Indefinite when resolving the helpers; propagate the
intended available space consistently with the block/float paths, or explicitly
document and correctly propagate any intentionally different spaces.
In `@Libraries/LibWeb/Rust/src/layout/svg_formatting_context.rs`:
- Around line 713-715: Update the RootSizingDirectives initialization used by
layout_inside_child for the transformed SVGForeignObjectBox to set both forced
content sizes from the CSS-pixel dimensions calculated around lines 700-701,
instead of using RootSizingDirectives::default(). Preserve the existing
ParticipationInParentFormattingContext::Item setting so the child HTML
formatting context uses the transformed SVG viewport dimensions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: cd1f8387-2745-470c-b447-14601254e504
📒 Files selected for processing (15)
Libraries/LibWeb/DOM/Document.cppLibraries/LibWeb/Layout/LayoutRustBridge.cppLibraries/LibWeb/Layout/LayoutRustBridge.hLibraries/LibWeb/Rust/src/layout/abspos_engine.rsLibraries/LibWeb/Rust/src/layout/block_formatting_context.rsLibraries/LibWeb/Rust/src/layout/flex_formatting_context.rsLibraries/LibWeb/Rust/src/layout/formatting_context.rsLibraries/LibWeb/Rust/src/layout/geometry.rsLibraries/LibWeb/Rust/src/layout/grid_formatting_context.rsLibraries/LibWeb/Rust/src/layout/inline_formatting_context.rsLibraries/LibWeb/Rust/src/layout/layout_state.rsLibraries/LibWeb/Rust/src/layout/replaced_with_children_formatting_context.rsLibraries/LibWeb/Rust/src/layout/sizing_context.rsLibraries/LibWeb/Rust/src/layout/svg_formatting_context.rsLibraries/LibWeb/Rust/src/layout/table_formatting_context.rs
💤 Files with no reviewable changes (2)
- Libraries/LibWeb/DOM/Document.cpp
- Libraries/LibWeb/Rust/src/layout/layout_state.rs
| self.resolve_used_block_size_if_not_treated_as_auto(node, available_space, input.containing_block_constraints); | ||
| let facts = self.facts(node); | ||
| if facts.has_auto_content_box_size() || self.style(node).display().is_flex_inside() { | ||
| if self.facts(node).has_auto_content_box_size() || self.style(node).display().is_flex_inside() { | ||
| self.resolve_used_block_size_if_treated_as_auto( | ||
| node, | ||
| available_space, | ||
| input.containing_block_constraints, | ||
| None, | ||
| ); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Float roots skip the quirks-mode block-size resolution space.
dimension_float_root resolves block sizes against input.available_space. The block-level path resolves them against self.sizing().available_space_for_block_size_resolution(...) (Lines 1658-1662 and Lines 2300-2305), which substitutes the quirks-mode percentage basis for a percentage height. A floating box with height: <percentage> in quirks mode therefore resolves against a different basis than an equivalent non-floating box.
Please confirm this difference is intended. If it is not, use the same helper here.
♻️ Proposed alignment with the block-level path
- pub(crate) fn dimension_float_root(&self, node: Node, input: &LayoutInput) {
- let available_space = input.available_space;
+ pub(crate) fn dimension_float_root(&self, node: Node, input: &LayoutInput) {
+ let available_space = input.available_space;
+ let resolution_space = self.sizing().available_space_for_block_size_resolution(
+ node,
+ available_space,
+ input.containing_block_constraints,
+ );- self.resolve_used_block_size_if_not_treated_as_auto(node, available_space, input.containing_block_constraints);
+ self.resolve_used_block_size_if_not_treated_as_auto(node, resolution_space, input.containing_block_constraints);
if self.facts(node).has_auto_content_box_size() || self.style(node).display().is_flex_inside() {
self.resolve_used_block_size_if_treated_as_auto(
node,
- available_space,
+ resolution_space,
input.containing_block_constraints,
None,
);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| self.resolve_used_block_size_if_not_treated_as_auto(node, available_space, input.containing_block_constraints); | |
| let facts = self.facts(node); | |
| if facts.has_auto_content_box_size() || self.style(node).display().is_flex_inside() { | |
| if self.facts(node).has_auto_content_box_size() || self.style(node).display().is_flex_inside() { | |
| self.resolve_used_block_size_if_treated_as_auto( | |
| node, | |
| available_space, | |
| input.containing_block_constraints, | |
| None, | |
| ); | |
| } | |
| let resolution_space = self.sizing().available_space_for_block_size_resolution( | |
| node, | |
| available_space, | |
| input.containing_block_constraints, | |
| ); | |
| self.resolve_used_block_size_if_not_treated_as_auto( | |
| node, | |
| resolution_space, | |
| input.containing_block_constraints, | |
| ); | |
| if self.facts(node).has_auto_content_box_size() || self.style(node).display().is_flex_inside() { | |
| self.resolve_used_block_size_if_treated_as_auto( | |
| node, | |
| resolution_space, | |
| input.containing_block_constraints, | |
| None, | |
| ); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs` around lines
2340 - 2348, Align the float-root block-size resolution in dimension_float_root
with the block-level path by passing
self.sizing().available_space_for_block_size_resolution(...) instead of
input.available_space where resolving used block sizes. Preserve the existing
auto-size branching and ensure quirks-mode percentage height uses the same
containing-block basis as non-floating boxes.
| fn size_skipped_independent_root( | ||
| run: &FormattingContextRun, | ||
| parent_block: Option<&BlockFormattingContext>, | ||
| child: Node, | ||
| input: &LayoutInput, | ||
| ) { | ||
| match input.participation { | ||
| ParticipationInParentFormattingContext::BlockLevel => { | ||
| let parent = parent_block.expect("a block-level run requires an enclosing block formatting context"); | ||
| parent.commit_block_level_root_inline_size(child, input); | ||
| parent.resolve_block_level_root_block_size_before_body(child, input); | ||
| } | ||
| ParticipationInParentFormattingContext::Float => { | ||
| let parent = parent_block.expect("a floating run requires an enclosing block formatting context"); | ||
| parent.dimension_float_root(child, input); | ||
| parent.finalize_float_root(child, input, None); | ||
| } | ||
| ParticipationInParentFormattingContext::AbsolutelyPositioned(abspos_inputs) => { | ||
| let engine = AbsposEngine::new(run.state, run.callbacks); | ||
| engine.dimension_out_of_flow_root(child, abspos_inputs); | ||
| engine.finalize_out_of_flow_root_after_inside_layout(child, abspos_inputs); | ||
| } | ||
| ParticipationInParentFormattingContext::AtomicInline | ParticipationInParentFormattingContext::Item | ParticipationInParentFormattingContext::Root => {} | ||
| } | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect the two block-level root sizing hooks used by the skipped path.
ast-grep run --pattern $'fn resolve_block_level_root_block_size_before_body($$$) {
$$$
}' --lang rust Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs
ast-grep run --pattern $'fn commit_block_level_root_inline_size($$$) {
$$$
}' --lang rust Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs
# Confirm whether resolve_used_block_size_if_treated_as_auto is reachable for childless block-level roots.
rg -n -C 5 'resolve_used_block_size_if_treated_as_auto' Libraries/LibWeb/Rust/src/layoutRepository: LadybirdBrowser/ladybird
Length of output: 14138
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== formatting_context relevant ranges =="
sed -n '1330,1410p' Libraries/LibWeb/Rust/src/layout/formatting_context.rs
echo
sed -n '1545,1640p' Libraries/LibWeb/Rust/src/layout/formatting_context.rs
echo
echo "== block formatting context block-size resolution hook =="
sed -n '755,795p' Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs
echo
sed -n '1790,1820p' Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs
echo
echo "== sizing hook =="
sed -n '957,1055p' Libraries/LibWeb/Rust/src/layout/sizing_context.rs
echo
echo "== deterministic call-site / predicate probe =="
python3 - <<'PY'
from pathlib import Path
p = Path('Libraries/LibWeb/Rust/src/layout/formatting_context.rs')
s = p.read_text()
for target in [
'size_skipped_independent_root',
'layout_inside_child',
'size_block_level_root',
'commit_block_level_root_inline_size',
'resolve_block_level_root_block_size_before_body',
'resolve_used_block_size_if_treated_as_auto',
'finalize_block_level_root',
'layout_inside',
]:
idx = s.find(target)
print(f'{target}: {idx}')
# extract lines and show whether each implementation contains specific calls
for line, name in enum:
pass
PY
rg -n -C 8 "size_skipped_independent_root|layout_inside_child|layout_inside\\(" Libraries/LibWeb/Rust/src/layout/formatting_context.rsRepository: LadybirdBrowser/ladybird
Length of output: 16366
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== BlockFormattingContext functions =="
rg -n -C 6 "pub\(crate\) fn commit_block_level_root_inline_size|fn resolve_block_level_root_block_size_before_body" Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs
echo
echo "== sizing.should_treat_block_size_as_auto implementation =="
rg -n -C 8 "fn should_treat_block_size_as_auto" Libraries/LibWeb/Rust/src/layout/sizing_context.rs
echo
echo "== SizingContext available_space_for_block_size_resolution =="
rg -n -C 6 "available_space_for_block_size_resolution" Libraries/LibWeb/Rust/src/layout/sizing_context.rs | sed -n '1,80p'Repository: LadybirdBrowser/ladybird
Length of output: 2741
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== BlockFormattingContext sizing methods surrounding finalize hooks =="
sed -n '950,1015p' Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs
echo
echo "== finalize_block_level_root and child_layout calls nearby =="
sed -n '1760,1830p' Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs
echo
echo "== all occurrences of finalize_block_level_root =="
rg -n -C 5 "finalize_block_level_root" Libraries/LibWeb/Rust/src/layoutRepository: LadybirdBrowser/ladybird
Length of output: 8563
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== exact finalize_block_level_root definition =="
sed -n '1343,1375p' Libraries/LibWeb/Rust/src/layout/formatting_context.rs
echo
echo "== exact finalize_block_level_root call sites =="
sed -n '1488,1535p' Libraries/LibWeb/Rust/src/layout/formatting_context.rs
sed -n '1612,1628p' Libraries/LibWeb/Rust/src/layout/formatting_context.rsRepository: LadybirdBrowser/ladybird
Length of output: 4564
Resolve the automatic block size for skipped block-level roots.
The skipped call path can still reach the second branch in layout_inside_child(), but size_skipped_independent_root() only resolves non-auto block sizes in resolve_block_level_root_block_size_before_body() and skips finalize_block_level_root(). Add the same auto block-size resolution finalize_block_level_root() uses when skipping only definite inline/block-size cases.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Libraries/LibWeb/Rust/src/layout/formatting_context.rs` around lines 1379 -
1403, Update size_skipped_independent_root’s BlockLevel branch to resolve
automatic block size for skipped roots, matching the auto-size handling in
finalize_block_level_root() while retaining the existing definite-size
resolution performed by resolve_block_level_root_block_size_before_body().
| let inline_definite_space = AvailableSpace { | ||
| inline_size: AvailableSize::definite(inline_size), | ||
| block_size: AvailableSize::Indefinite, | ||
| }; | ||
| self.resolve_used_block_size_if_not_treated_as_auto(node, inline_definite_space, constraints); | ||
| if style.display().is_flex_inside() { | ||
| // Flex containers with an automatic block size are treated as max-content, so resolve it early. | ||
| self.resolve_used_block_size_if_treated_as_auto(node, inline_definite_space, constraints, None, || { | ||
| crate::layout::independent_root_automatic_block_size( | ||
| self.state, | ||
| &self.callbacks, | ||
| node, | ||
| self.used(node) | ||
| .available_inner_space_or_constraints_from(inline_definite_space), | ||
| constraints, | ||
| ) | ||
| }); | ||
| } | ||
| self.make_button_content_box_definite(node, layout_mode, available_space, constraints, None); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Compare with the block-level root sizing path, which is the closest analogue.
ast-grep run --pattern $'fn dimension_block_level_root($$$) -> $_ {
$$$
}' --lang rust Libraries/LibWeb/Rust/src/layout/formatting_context.rs
# Check whether any caller of dimension_atomic_root supplies an intrinsic block available size.
rg -n -C 8 '\bdimension_atomic_root\s*\(' Libraries/LibWeb/Rust/src/layoutRepository: LadybirdBrowser/ladybird
Length of output: 7293
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## dimension_atomic_root section"
sed -n '1038,1175p' Libraries/LibWeb/Rust/src/layout/sizing_context.rs
echo
echo "## dimension_block_level_root section"
sed -n '1308,1346p' Libraries/LibWeb/Rust/src/layout/formatting_context.rs
echo
echo "## predicates definitions and uses"
rg -n -C 5 'should_treat_block_size_as_auto|should_treat_max_block_size_as_none|make_button_content_box_definite|calculate_inner_block_size' Libraries/LibWeb/Rust/src/layout/sizing_context.rs Libraries/LibWeb/Rust/src/layout/formatting_context.rs
echo
echo "## callers of dimension_atomic_root"
rg -n -C 4 '\bdimension_atomic_root\s*\(' Libraries/LibWeb/Rust/srcRepository: LadybirdBrowser/ladybird
Length of output: 46509
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## AtomicInline partitioning code"
sed -n '1180,1295p' Libraries/LibWeb/Rust/src/layout/formatting_context.rs
echo
echo "## available space creation/partitioning paths"
rg -n -C 5 'AtomicInline|atomic inline|input.available_space|available_space.block_size|layout_input|LayoutInput::new|DimensionRoot|dimension.*root' Libraries/LibWeb/Rust/src/layout/formatting_context.rs Libraries/LibWeb/Rust/src/layout/sizing_context.rs
echo
echo "## intrinsic available-space helper implementation"
sed -n '1296,1308p' Libraries/LibWeb/Rust/src/layout/formatting_context.rs
echo
echo "## structural probe: predicates around inline_definite_space calls"
python3 - <<'PY'
from pathlib import Path
p = Path('Libraries/LibWeb/Rust/src/layout/sizing_context.rs')
text = p.read_text()
start = text.index('pub(crate) fn dimension_atomic_root')
end = text.index('fn calculate_stretch_fit_inline_size', start)
block = text[start:end]
for name in [
'should_treat_block_size_as_auto(node, inline_definite_space, constraints)',
'should_treat_block_size_as_auto(node, available_space, constraints)',
'should_treat_max_block_size_as_none(node, inline_definite_space.block_size, constraints)',
'should_treat_max_block_size_as_none(node, available_space.block_size, constraints)',
]:
print(f'{name}:', name in block)
PYRepository: LadybirdBrowser/ladybird
Length of output: 44236
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## finalize_atomic_root_block_size"
sed -n '1548,1590p' Libraries/LibWeb/Rust/src/layout/formatting_context.rs
echo
echo "## atomic intrinsic blocks"
rg -n -C 6 'fn compute_block_size_for_replaced_element|fn should_treat_block_size_as_auto|fn calculate_inner_block_size|fn make_button_content_box_definite|compute.*block.*size|preferred_available_space|available_inner_space_or_constraints_from|ParticipationInParentFormattingContext::AtomicInline|AvailableSize::intrinsic' Libraries/LibWeb/Rust/src/layout/sizing_context.rs Libraries/LibWeb/Rust/src/layout/formatting_context.rs | sed -n '1,260p'Repository: LadybirdBrowser/ladybird
Length of output: 28120
Keep the block-axis intrinsic constraint in atomic inline root sizing.
dimension_atomic_root passes input.available_space.block_size back to finalize_atomic_root_block_size, but the block-size resolution helpers run under AvailableSize::Indelinite. An atomic inline under min-content/max-content/block-content sizing will therefore have its block-space constraint treated away, unlike the block/float paths that pass the original available space through. Preserve the block-space constraint instead of forcing Indefinite, or document/propagate the intended different spaces.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Libraries/LibWeb/Rust/src/layout/sizing_context.rs` around lines 1136 - 1155,
Update the atomic inline root sizing flow around dimension_atomic_root and
finalize_atomic_root_block_size so block-axis intrinsic constraints from
input.available_space.block_size are preserved through block-size resolution. Do
not replace the original block available space with AvailableSize::Indefinite
when resolving the helpers; propagate the intended available space consistently
with the block/float paths, or explicitly document and correctly propagate any
intentionally different spaces.
| sizing: RootSizingDirectives::default(), | ||
| participation: ParticipationInParentFormattingContext::Item, | ||
| }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Force the transformed foreign-object root size.
Lines 700-701 calculate the CSS-pixel size for the transformed SVGForeignObjectBox. layout_inside_child then starts an independent child run. RootSizingDirectives::default() does not preserve those dimensions as the child root size.
Set both forced content sizes. This keeps the HTML formatting context aligned with the SVG viewport after transforms.
Proposed fix
- sizing: RootSizingDirectives::default(),
+ sizing: RootSizingDirectives {
+ forced_content_inline_size: Some(transformed_rect.width),
+ forced_content_block_size: Some(transformed_rect.height),
+ ..RootSizingDirectives::default()
+ },📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| sizing: RootSizingDirectives::default(), | |
| participation: ParticipationInParentFormattingContext::Item, | |
| }; | |
| sizing: RootSizingDirectives { | |
| forced_content_inline_size: Some(transformed_rect.width), | |
| forced_content_block_size: Some(transformed_rect.height), | |
| ..RootSizingDirectives::default() | |
| }, | |
| participation: ParticipationInParentFormattingContext::Item, |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Libraries/LibWeb/Rust/src/layout/svg_formatting_context.rs` around lines 713
- 715, Update the RootSizingDirectives initialization used by
layout_inside_child for the transformed SVGForeignObjectBox to set both forced
content sizes from the CSS-pixel dimensions calculated around lines 700-701,
instead of using RootSizingDirectives::default(). Preserve the existing
ParticipationInParentFormattingContext::Item setting so the child HTML
formatting context uses the transformed SVG viewport dimensions.
c344975
into
LadybirdBrowser:master
Root box sizing used to be the launching context's job: the block, inline, and abspos parents each computed a child's used sizes around its inner layout, the table context sized itself, and a two-phase finish callback plus a pre-created used-values whitelist papered over the places where that split leaked.
Now sizes flow down as constraints and come back up as results. LayoutInput carries the containing block's available space and percentage bases, how the root participates in its parent's layout (block-level, float, atomic inline, out-of-flow, item, or entry root), and parent-authoritative directives such as forced viewport sizes and the float-avoidance inline space. Each formatting-context run sizes its own root in a prelude, lays out its contents against the root's inner space, resolves the automatic block size in an epilogue, and returns plain results the parent uses to place the box. The parent probes float avoidance with the same non-committing sizing function the prelude later commits, so measure and commit cannot diverge. The completion protocol, the whitelist, and the table-offset side channel are gone.