mirror of
https://github.com/lexmount/moli.git
synced 2026-10-08 00:01:00 +00:00
Fix table cell baselines and float vertical alignment
This commit is contained in:
Generated
+1
-1
@@ -5155,7 +5155,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "taffy"
|
||||
version = "0.13.0"
|
||||
source = "git+https://github.com/ldm0/taffy?rev=acc16f5b0d570656c504a44b52f1736f05991a40#acc16f5b0d570656c504a44b52f1736f05991a40"
|
||||
source = "git+https://github.com/ldm0/taffy?rev=645bf5a49df607a8bff34ec90c5343901d04f10e#645bf5a49df607a8bff34ec90c5343901d04f10e"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"serde",
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ stylo_atoms = { git = "https://github.com/ldm0/stylo", rev = "671d13d31b3b00e7b1
|
||||
stylo_dom = { git = "https://github.com/ldm0/stylo", rev = "671d13d31b3b00e7b1e0088ba2b36a4e35c0858c" }
|
||||
stylo_static_prefs = { git = "https://github.com/ldm0/stylo", rev = "671d13d31b3b00e7b1e0088ba2b36a4e35c0858c" }
|
||||
stylo_traits = { git = "https://github.com/ldm0/stylo", rev = "671d13d31b3b00e7b1e0088ba2b36a4e35c0858c" }
|
||||
taffy = { git = "https://github.com/ldm0/taffy", rev = "acc16f5b0d570656c504a44b52f1736f05991a40" }
|
||||
taffy = { git = "https://github.com/ldm0/taffy", rev = "645bf5a49df607a8bff34ec90c5343901d04f10e" }
|
||||
|
||||
[workspace.lints.clippy]
|
||||
disallowed_methods = "deny"
|
||||
|
||||
@@ -241,7 +241,7 @@ impl TableContext {
|
||||
let natural = mode.to_logical(output.size).block_size;
|
||||
let cell = &mut self.cells[index];
|
||||
let baseline = if mode.is_horizontal() {
|
||||
output.first_baselines.y
|
||||
output.first_baselines.y.or(output.block_content_end)
|
||||
} else {
|
||||
output.first_baselines.x
|
||||
};
|
||||
@@ -532,44 +532,13 @@ where
|
||||
world.compute_child_layout(cell.to_taffy(), inputs)
|
||||
};
|
||||
world.table_cell_percentage_height = previous;
|
||||
let free = (layout.size - mode.to_logical(output.size).block_size).max(0.0);
|
||||
let alignment = world.boxes[cell.index()].style.taffy.align_content;
|
||||
let offset = if layout.baseline.is_some() {
|
||||
0.0
|
||||
} else {
|
||||
crate::taffy_tree::single_subject_block_alignment_offset(alignment, free)
|
||||
};
|
||||
if inputs.run_mode == RunMode::PerformLayout && offset != 0.0 {
|
||||
shift_cell_contents(world, cell, offset);
|
||||
}
|
||||
if let Some(baseline) = &mut output.first_baselines.y {
|
||||
*baseline += offset;
|
||||
}
|
||||
if let Some(baseline) = &mut output.last_baselines.y {
|
||||
*baseline += offset;
|
||||
}
|
||||
if layout.baseline.is_some() && output.first_baselines.y.is_none() {
|
||||
// Cells with no line baseline use the content's block-end edge. This
|
||||
// must be sampled again after percentage descendants have laid out.
|
||||
let padding = world.boxes[cell.index()]
|
||||
.style
|
||||
.taffy
|
||||
.padding
|
||||
.resolve_or_zero(inputs.parent_size.width, resolve_stylo_calc_value);
|
||||
output.first_baselines.y = Some(if !layout.definite {
|
||||
let border = world.boxes[cell.index()]
|
||||
.style
|
||||
.taffy
|
||||
.border
|
||||
.resolve_or_zero(inputs.parent_size.width, resolve_stylo_calc_value);
|
||||
// The natural border box includes empty block children whose
|
||||
// overflow content size can be zero (for example an empty group).
|
||||
(layout.natural_size - padding.bottom - border.bottom).max(0.0)
|
||||
} else {
|
||||
(output.content_size.height - padding.bottom).max(0.0)
|
||||
});
|
||||
// This is measured again with final percentage constraints, but before
|
||||
// relative positioning or absolute descendants contribute overflow.
|
||||
output.first_baselines.y = output.block_content_end;
|
||||
}
|
||||
output.content_size.height += offset;
|
||||
// Block/inline layout has already aligned the content group inside this
|
||||
// final geometry. Only baseline alignment needs a later table-wide shift.
|
||||
set_block(mode, &mut output.size, layout.size);
|
||||
output
|
||||
}
|
||||
|
||||
@@ -1766,6 +1766,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn block_alignment_includes_floats(&self, node_id: NodeId) -> bool {
|
||||
!self.is_viewport_taffy_node(node_id)
|
||||
&& matches!(
|
||||
self.boxes[LayoutBoxId::from_taffy(node_id).index()].kind,
|
||||
LayoutBoxKind::TableCell | LayoutBoxKind::AnonymousTableCell
|
||||
)
|
||||
}
|
||||
|
||||
fn compute_block_child_layout(
|
||||
&mut self,
|
||||
node_id: NodeId,
|
||||
@@ -2202,6 +2210,13 @@ where
|
||||
content_box_height - measurement.alignment_block_size,
|
||||
);
|
||||
measurement.translate_block_axis(block_offset);
|
||||
output.block_content_end = Some(
|
||||
measurement.alignment_block_size
|
||||
+ padding.top
|
||||
+ border.top
|
||||
+ scrollbar_insets.top
|
||||
+ block_offset,
|
||||
);
|
||||
output.content_size.height = output.content_size.height.max(
|
||||
measurement.alignment_block_size
|
||||
+ padding.top
|
||||
|
||||
@@ -110,6 +110,10 @@ impl<N: Copy + Debug + Eq + Hash> LayoutBlockContainer for FullMeasurementTree<'
|
||||
.get_block_percentage_resolution_height(node_id, height)
|
||||
}
|
||||
|
||||
fn block_alignment_includes_floats(&self, node_id: NodeId) -> bool {
|
||||
self.0.block_alignment_includes_floats(node_id)
|
||||
}
|
||||
|
||||
fn compute_block_child_layout(
|
||||
&mut self,
|
||||
node_id: NodeId,
|
||||
|
||||
@@ -2950,6 +2950,105 @@ return [outer.height,rect('inner').height,rect('percent').height,
|
||||
}).await.expect("nested percentage baseline should match Chromium");
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn table_cell_synthetic_baselines_ignore_positioned_overflow() {
|
||||
run_page_vm_async_test(async move {
|
||||
let loader = crate::network::ResourceRequestClient::new(&FetchConfig::default())?;
|
||||
let mut page = test_page_vm_with_loader_and_document_url(
|
||||
&loader,
|
||||
Vec::new(),
|
||||
Url::parse("https://example.com/table-cell-flow-baseline.html")?,
|
||||
);
|
||||
page.vm_mut().set_layout_policy(moli_page_types::LayoutPolicy::OnDemand);
|
||||
page.vm_mut().eval(r#"
|
||||
document.head.innerHTML = `<style>
|
||||
body { margin:0 } table { width:200px;border-spacing:0;table-layout:fixed }
|
||||
td { padding:0;font-size:0;line-height:0;vertical-align:baseline }
|
||||
</style>`;
|
||||
document.body.innerHTML = `<table><tr>
|
||||
<td id=cell style="height:100px;position:relative">
|
||||
<div id=content style="height:10px;width:10px"></div>
|
||||
<div id=overlay style="position:absolute;inset:0"></div>
|
||||
</td><td><span id=peer style="display:inline-block;height:20px;width:10px"></span></td>
|
||||
</tr></table>`;
|
||||
"#)?;
|
||||
for relative in [false, true, false] {
|
||||
for height in [100, 160, 100] {
|
||||
page.vm_mut().eval(&format!(r#"
|
||||
document.getElementById('cell').style.height='{height}px';
|
||||
document.getElementById('overlay').style.display='{}';
|
||||
document.getElementById('content').style.cssText='height:10px;width:10px;{}';
|
||||
"#, if relative { "none" } else { "block" }, if relative { "position:relative;top:50%" } else { "" }))?;
|
||||
for _ in 0..2 {
|
||||
page.vm_mut().screenshot_layout_snapshot(moli_layout::PaintViewport::new(320, 240, 1.0))?.expect("table root");
|
||||
let actual = page.vm_mut().eval(r#"
|
||||
['cell','peer','content','overlay'].map(id => {
|
||||
const r=document.getElementById(id).getBoundingClientRect(); return [r.y,r.height].join(',');
|
||||
}).join('|')
|
||||
"#)?;
|
||||
let content_y = 10 + if relative { height / 2 } else { 0 };
|
||||
let overlay_height = if relative { 0 } else { height };
|
||||
assert_eq!(actual, format!("0,{height}|0,20|{content_y},10|0,{overlay_height}"),
|
||||
"relative={relative}: synthetic baselines use normal-flow geometry, while positioned descendants retain final cell geometry");
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok::<_, anyhow::Error>(())
|
||||
}).await.expect("positioned overflow must not move adjacent cell baselines");
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn table_cell_vertical_alignment_includes_floats() {
|
||||
run_page_vm_async_test(async move {
|
||||
let loader = crate::network::ResourceRequestClient::new(&FetchConfig::default())?;
|
||||
let mut page = test_page_vm_with_loader_and_document_url(
|
||||
&loader,
|
||||
Vec::new(),
|
||||
Url::parse("https://example.com/table-cell-float-alignment.html")?,
|
||||
);
|
||||
page.vm_mut().set_layout_policy(moli_page_types::LayoutPolicy::OnDemand);
|
||||
page.vm_mut().eval(r#"
|
||||
document.head.innerHTML = `<style>
|
||||
body { margin:0 } table { width:200px;border-spacing:0;table-layout:fixed }
|
||||
td { padding:0;font-size:0;line-height:0 }
|
||||
</style>`;
|
||||
document.body.innerHTML = `<table><tr>
|
||||
<td id=cell style="position:relative">
|
||||
<div id=float style="float:left;width:10px;height:30px"></div>
|
||||
<div id=block style="margin-left:15px;width:10px;height:20px"></div>
|
||||
<div id=overlay style="position:absolute;inset:0"></div>
|
||||
</td><td><div id=tall></div></td></tr></table>`;
|
||||
"#)?;
|
||||
for align in ["middle", "bottom"] {
|
||||
for mixed in [false, true] {
|
||||
for padding in [0, 5] {
|
||||
for height in [100, 160, 100] {
|
||||
page.vm_mut().eval(&format!(r#"
|
||||
document.getElementById('cell').style.cssText='position:relative;vertical-align:{align};padding:{padding}px';
|
||||
document.getElementById('block').style.display='{}';
|
||||
document.getElementById('tall').style.height='{height}px';
|
||||
"#, if mixed { "block" } else { "none" }))?;
|
||||
for _ in 0..2 {
|
||||
page.vm_mut().screenshot_layout_snapshot(moli_layout::PaintViewport::new(320, 240, 1.0))?.expect("table root");
|
||||
let actual = page.vm_mut().eval(r#"
|
||||
['cell','float','block','overlay'].map(id => {
|
||||
const r=document.getElementById(id).getBoundingClientRect(); return [r.y,r.height].join(',');
|
||||
}).join('|')
|
||||
"#)?;
|
||||
let free = height - 2 * padding - 30;
|
||||
let y = padding + if align == "middle" { free / 2 } else { free };
|
||||
let block = if mixed { format!("{y},20") } else { "0,0".to_owned() };
|
||||
assert_eq!(actual, format!("0,{height}|{y},30|{block}|0,{height}"),
|
||||
"{align}, mixed={mixed}, padding={padding}: align floats and normal flow together without shifting the absolute overlay");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok::<_, anyhow::Error>(())
|
||||
}).await.expect("table-cell vertical alignment must include floats");
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn screenshot_table_row_heights_match_chromium() {
|
||||
run_page_vm_async_test(async move {
|
||||
|
||||
Reference in New Issue
Block a user