render: correct list and mixed float geometry

This commit is contained in:
SGavrl committed 2026-07-23 18:42:15 +00:00
1 parent 3863833ca4
commit eff74fc38c
3 files changed
+90 -7

No files matched your search

+68 -1
View File
@@ -146,6 +146,7 @@ pub(crate) fn resolve_translate(d: crate::Dimension, basis: f32) -> f32 {
crate::Dimension::Px(px) => px,
crate::Dimension::Percent(p) => p * basis,
crate::Dimension::Em(v) | crate::Dimension::Rem(v) => v * 16.0,
crate::Dimension::Ex(v) => v * 16.0 * 0.528_320_3,
crate::Dimension::Vw(v)
| crate::Dimension::Vh(v)
| crate::Dimension::Vmin(v)
@@ -2860,10 +2861,76 @@ fn build_children_with_float_zone(
}
}
if float_count >= 2 {
let run_children: Vec<taffy::NodeId> = dom_children[float_idx..run_end]
let mut run_children: Vec<taffy::NodeId> = dom_children[float_idx..run_end]
.iter()
// Formatting whitespace between floats does not generate an
// in-flow flex item or consume horizontal space.
.filter(|&&cid| styles.get(&cid).and_then(|s| s.float) == float_side)
.flat_map(|&cid| build_any(tree, cid, taffy_tree, id_map, words, engine, ifc_items, styles))
.collect();
// A common navigation-bar shape is a run of left floats followed by
// one right float. The right float still scans the current float band:
// it does not start a new row merely because multiple left floats
// precede it. Keep the run in one wrapping row and use an auto inline
// margin to reserve all remaining space before the opposing float.
//
// This stays deliberately narrower than a general float manager. In
// particular, multiple right floats have reverse source-order
// placement semantics and need their own representation.
let trailing_right = (float_side == Some(crate::Float::Left)
&& dom_children
.get(run_end)
.and_then(|cid| styles.get(cid))
.and_then(|style| style.float)
== Some(crate::Float::Right))
.then(|| dom_children[run_end]);
if let Some(right_dom) = trailing_right {
let right = build(
tree,
right_dom,
taffy_tree,
id_map,
words,
engine,
ifc_items,
styles,
);
if let Some(right) = right {
if let Ok(current) = taffy_tree.style(right) {
let mut pushed_right = current.clone();
pushed_right.margin.left =
taffy::style::LengthPercentageAuto::auto();
let _ = taffy_tree.set_style(right, pushed_right);
}
run_children.push(right);
let row_style = taffy::Style {
display: taffy::style::Display::Flex,
flex_direction: taffy::FlexDirection::Row,
flex_wrap: taffy::FlexWrap::Wrap,
align_items: Some(taffy::AlignItems::FLEX_START),
size: taffy::Size {
width: taffy::Dimension::percent(1.0),
height: taffy::Dimension::auto(),
},
..Default::default()
};
if let Ok(row) = taffy_tree.new_with_children(row_style, &run_children) {
result.push(row);
}
result.extend(build_children_with_float_zone(
tree,
parent_id,
&dom_children[run_end + 1..],
taffy_tree,
id_map,
words,
engine,
ifc_items,
styles,
));
return result;
}
}
let row_style = taffy::Style {
display: taffy::style::Display::Flex,
flex_direction: taffy::FlexDirection::Row,
+8 -1
View File
@@ -133,9 +133,11 @@ pub enum Dimension {
/// Font-relative and viewport-relative units, kept unresolved at parse
/// time (the element font-size and viewport are not known then) and
/// resolved to `Px` during `dom::layout_dom`'s top-down pass via
/// [`Dimension::resolve`]. Resolving em/rem/vw against a hardcoded 16px at
/// [`Dimension::resolve`]. Resolving font/viewport units against a
/// hardcoded 16px at
/// parse time (the old behavior) silently corrupted every relative length.
Em(f32),
Ex(f32),
Rem(f32),
Vw(f32),
Vh(f32),
@@ -151,6 +153,10 @@ impl Dimension {
pub fn resolve(self, em_px: f32, rem_px: f32, vw: f32, vh: f32) -> Dimension {
match self {
Dimension::Em(v) => Dimension::Px(v * em_px),
// Liberation Sans is the deterministic generic sans face used by
// the renderer. This is its x-height as a fraction of the em,
// matching Chromium's generic sans face on the capture host.
Dimension::Ex(v) => Dimension::Px(v * em_px * 0.528_320_3),
Dimension::Rem(v) => Dimension::Px(v * rem_px),
Dimension::Vw(v) => Dimension::Px(v * vw),
Dimension::Vh(v) => Dimension::Px(v * vh),
@@ -813,6 +819,7 @@ fn dimension(v: Dimension) -> taffy::style::Dimension {
// through unresolved, fall back to its raw magnitude (em/rem ~16px)
// rather than panicking.
Dimension::Em(v) | Dimension::Rem(v) => taffy::style::Dimension::length(v * 16.0),
Dimension::Ex(v) => taffy::style::Dimension::length(v * 16.0 * 0.528_320_3),
Dimension::Vw(v) | Dimension::Vh(v) | Dimension::Vmin(v) | Dimension::Vmax(v) => taffy::style::Dimension::length(v),
}
}
+14 -5
View File
@@ -91,15 +91,15 @@ pub fn ua_style(tag: &str) -> LayoutStyle {
style.font_size = Some(16.0);
style.font_weight = Some("bold".to_string());
style.margin = Edges { top: 21.0, bottom: 21.0, left: 0.0, right: 0.0 };
} else if tag == "p" || tag == "ul" || tag == "ol" {
} else if matches!(tag, "p" | "ul" | "ol" | "menu" | "dir") {
style.margin = Edges { top: 16.0, bottom: 16.0, left: 0.0, right: 0.0 };
if tag == "ul" {
if matches!(tag, "ul" | "menu" | "dir") {
style.list_style = Some(crate::ListStyle::Disc);
style.padding.left = 40.0;
} else if tag == "ol" {
style.list_style = Some(crate::ListStyle::Decimal);
style.padding.left = 40.0;
}
} else if tag == "li" {
style.margin = Edges { top: 0.0, bottom: 0.0, left: 40.0, right: 0.0 };
} else if tag == "b" || tag == "strong" {
style.font_weight = Some("bold".to_string());
} else if tag == "i" || tag == "em" || tag == "cite" || tag == "var" || tag == "dfn" || tag == "address" {
@@ -1757,6 +1757,9 @@ fn contextual_atom(value: &str, context: &LengthContext) -> Option<f32> {
if let Some(value) = lower.strip_suffix("em").and_then(parse) {
return Some(value * context.em_px);
}
if let Some(value) = lower.strip_suffix("ex").and_then(parse) {
return Some(value * context.em_px * 0.528_320_3);
}
if let Some(value) = lower.strip_suffix("vmin").and_then(parse) {
return Some(value * context.vw.min(context.vh));
}
@@ -2153,7 +2156,7 @@ fn is_font_size_token(value: &str) -> bool {
{
return true;
}
["px", "pt", "em", "rem", "vw", "vh", "vmin", "vmax", "%"]
["px", "pt", "em", "ex", "rem", "vw", "vh", "vmin", "vmax", "%"]
.iter()
.any(|unit| lower.strip_suffix(unit).and_then(|number| number.parse::<f32>().ok()).is_some())
}
@@ -2177,6 +2180,7 @@ fn dimension_value(tok: &str) -> crate::Dimension {
// Order matters: check `rem` before `em`, `vmin`/`vmax` before `vw`/`vh`.
if let Some(v) = lower.strip_suffix("rem").and_then(parse) { return Dimension::Rem(v); }
if let Some(v) = lower.strip_suffix("em").and_then(parse) { return Dimension::Em(v); }
if let Some(v) = lower.strip_suffix("ex").and_then(parse) { return Dimension::Ex(v); }
if let Some(v) = lower.strip_suffix("vmin").and_then(parse) { return Dimension::Vmin(v); }
if let Some(v) = lower.strip_suffix("vmax").and_then(parse) { return Dimension::Vmax(v); }
if let Some(v) = lower.strip_suffix("vw").and_then(parse) { return Dimension::Vw(v); }
@@ -2231,6 +2235,7 @@ fn set_margin_side(style: &mut LayoutStyle, idx: usize, value: &str) {
style.margin_relative[idx] = None;
}
crate::Dimension::Em(_)
| crate::Dimension::Ex(_)
| crate::Dimension::Rem(_)
| crate::Dimension::Vw(_)
| crate::Dimension::Vh(_)
@@ -2285,6 +2290,7 @@ fn set_padding_side(style: &mut LayoutStyle, idx: usize, value: &str) {
style.padding_percent[idx] = None;
}
crate::Dimension::Em(_)
| crate::Dimension::Ex(_)
| crate::Dimension::Rem(_)
| crate::Dimension::Vw(_)
| crate::Dimension::Vh(_)
@@ -2366,6 +2372,9 @@ fn px_value(tok: &str) -> Option<f32> {
} else if n.ends_with("em") || n.ends_with("rem") {
n = n.trim_end_matches(|c: char| c.is_ascii_alphabetic());
scale = 16.0; // 1em = 16px
} else if n.ends_with("ex") {
n = n.trim_end_matches(|c: char| c.is_ascii_alphabetic());
scale = 16.0 * 0.528_320_3;
} else if n.ends_with('%') {
n = &n[..n.len() - 1];
scale = 16.0 / 100.0;