Match font-relative UA block spacing
This commit is contained in:
1 parent
b0b4deeee5
commit
c68ca4dc1d
2 files changed
+173
-13
No files matched your search
@@ -1395,6 +1395,39 @@ fn cascade_walk(
|
|||||||
let mut style = crate::style::ua_style(elem.local.as_ref());
|
let mut style = crate::style::ua_style(elem.local.as_ref());
|
||||||
style.is_replaced_box = crate::inline::is_replaced(elem.local.as_ref());
|
style.is_replaced_box = crate::inline::is_replaced(elem.local.as_ref());
|
||||||
style.color_scheme_dark = inherited_color_scheme_dark;
|
style.color_scheme_dark = inherited_color_scheme_dark;
|
||||||
|
if matches!(elem.local.as_ref(), "dir" | "dl" | "menu" | "ol" | "ul") {
|
||||||
|
let mut list_ancestor_count = 0usize;
|
||||||
|
let mut has_list_or_definition_ancestor = false;
|
||||||
|
let mut ancestor = node.parent;
|
||||||
|
while let Some(ancestor_id) = ancestor {
|
||||||
|
let Some(ancestor_node) = tree.get_node(ancestor_id) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
if let Some(ancestor_element) = ancestor_node.as_element() {
|
||||||
|
let local = ancestor_element.local.as_ref();
|
||||||
|
if matches!(local, "dir" | "dl" | "menu" | "ol" | "ul") {
|
||||||
|
has_list_or_definition_ancestor = true;
|
||||||
|
}
|
||||||
|
if matches!(local, "dir" | "menu" | "ol" | "ul") {
|
||||||
|
list_ancestor_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ancestor = ancestor_node.parent;
|
||||||
|
}
|
||||||
|
if has_list_or_definition_ancestor {
|
||||||
|
style.margin.top = 0.0;
|
||||||
|
style.margin.bottom = 0.0;
|
||||||
|
style.margin_relative[0] = None;
|
||||||
|
style.margin_relative[2] = None;
|
||||||
|
}
|
||||||
|
if matches!(elem.local.as_ref(), "dir" | "menu" | "ul") {
|
||||||
|
style.list_style = match list_ancestor_count {
|
||||||
|
0 => Some(crate::ListStyle::Disc),
|
||||||
|
1 => Some(crate::ListStyle::Circle),
|
||||||
|
_ => Some(crate::ListStyle::Square),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
if matches!(elem.local.as_ref(), "td" | "th") {
|
if matches!(elem.local.as_ref(), "td" | "th") {
|
||||||
if let Some(padding) = inherited_cell_padding {
|
if let Some(padding) = inherited_cell_padding {
|
||||||
style.padding = crate::Edges {
|
style.padding = crate::Edges {
|
||||||
@@ -11185,6 +11218,91 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ua_block_margins_resolve_against_the_computed_element_font() {
|
||||||
|
let tree = parse_html(
|
||||||
|
r#"<style>
|
||||||
|
body { margin:0 }
|
||||||
|
h1 { font-size:40px }
|
||||||
|
p { font-size:24px }
|
||||||
|
</style>
|
||||||
|
<h1 id="heading">Heading</h1>
|
||||||
|
<p id="paragraph">Paragraph</p>
|
||||||
|
<section style="font-size:20px">
|
||||||
|
<h1 id="h1">H1</h1><h2 id="h2">H2</h2><h3 id="h3">H3</h3>
|
||||||
|
<h4 id="h4">H4</h4><h5 id="h5">H5</h5><h6 id="h6">H6</h6>
|
||||||
|
<h1 id="inherit-size" style="font-size:inherit">Inherited</h1>
|
||||||
|
<h1 id="initial-size" style="font-size:initial">Initial</h1>
|
||||||
|
</section>
|
||||||
|
<ul id="outer-list"><li>
|
||||||
|
<ul id="second-list"><li>
|
||||||
|
<ol><li><ul id="third-list"></ul></li></ol>
|
||||||
|
</li></ul>
|
||||||
|
</li></ul>
|
||||||
|
<dl><dd><ul id="definition-list-child"></ul></dd></dl>"#,
|
||||||
|
);
|
||||||
|
let laid = layout_dom(&tree, (800.0, 400.0));
|
||||||
|
let heading = tree.get_element_by_id("heading").unwrap();
|
||||||
|
let paragraph = tree.get_element_by_id("paragraph").unwrap();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
(laid.styles[&heading].margin.top - 26.8).abs() < 0.01
|
||||||
|
&& (laid.styles[&heading].margin.bottom - 26.8).abs() < 0.01,
|
||||||
|
"h1's 0.67em UA margins must follow its authored 40px font: {:?}",
|
||||||
|
laid.styles[&heading].margin
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
(laid.styles[¶graph].margin.top - 24.0).abs() < 0.01
|
||||||
|
&& (laid.styles[¶graph].margin.bottom - 24.0).abs() < 0.01,
|
||||||
|
"p's 1em UA margins must follow its authored 24px font: {:?}",
|
||||||
|
laid.styles[¶graph].margin
|
||||||
|
);
|
||||||
|
for (id, size, margin) in [
|
||||||
|
("h1", 40.0, 26.8),
|
||||||
|
("h2", 30.0, 24.9),
|
||||||
|
("h3", 23.4, 23.4),
|
||||||
|
("h4", 20.0, 26.6),
|
||||||
|
("h5", 16.6, 27.722),
|
||||||
|
("h6", 13.4, 31.222),
|
||||||
|
] {
|
||||||
|
let style = &laid.styles[&tree.get_element_by_id(id).unwrap()];
|
||||||
|
assert!(
|
||||||
|
(style.font_size.unwrap() - size).abs() < 0.01
|
||||||
|
&& (style.margin.top - margin).abs() < 0.01
|
||||||
|
&& (style.margin.bottom - margin).abs() < 0.01,
|
||||||
|
"{id} UA font and margins must resolve from the inherited 20px font: size={:?}, margin={:?}",
|
||||||
|
style.font_size,
|
||||||
|
style.margin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (id, size, margin) in [
|
||||||
|
("inherit-size", 20.0, 13.4),
|
||||||
|
("initial-size", 16.0, 10.72),
|
||||||
|
] {
|
||||||
|
let style = &laid.styles[&tree.get_element_by_id(id).unwrap()];
|
||||||
|
assert!(
|
||||||
|
(style.font_size.unwrap() - size).abs() < 0.01
|
||||||
|
&& (style.margin.top - margin).abs() < 0.01,
|
||||||
|
"{id} CSS-wide font-size semantics must override the UA heading size: size={:?}, margin={:?}",
|
||||||
|
style.font_size,
|
||||||
|
style.margin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (id, marker) in [
|
||||||
|
("second-list", crate::ListStyle::Circle),
|
||||||
|
("third-list", crate::ListStyle::Square),
|
||||||
|
("definition-list-child", crate::ListStyle::Disc),
|
||||||
|
] {
|
||||||
|
let style = &laid.styles[&tree.get_element_by_id(id).unwrap()];
|
||||||
|
assert_eq!(style.margin.top, 0.0, "{id} must lose nested UA margins");
|
||||||
|
assert_eq!(
|
||||||
|
style.margin.bottom, 0.0,
|
||||||
|
"{id} must lose nested UA margins"
|
||||||
|
);
|
||||||
|
assert_eq!(style.list_style, Some(marker), "{id} marker depth");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generated_inline_boxes_apply_sizes_only_after_display_blockification() {
|
fn generated_inline_boxes_apply_sizes_only_after_display_blockification() {
|
||||||
let tree = parse_html(
|
let tree = parse_html(
|
||||||
|
|||||||
@@ -73,23 +73,44 @@ pub fn ua_style(tag: &str) -> LayoutStyle {
|
|||||||
} else if tag == "body" {
|
} else if tag == "body" {
|
||||||
style.margin = Edges { top: 8.0, right: 8.0, bottom: 8.0, left: 8.0 };
|
style.margin = Edges { top: 8.0, right: 8.0, bottom: 8.0, left: 8.0 };
|
||||||
} else if tag == "h1" {
|
} else if tag == "h1" {
|
||||||
style.font_size = Some(32.0);
|
style.font_size = None;
|
||||||
|
style.font_size_raw = Some(crate::Dimension::Em(2.0));
|
||||||
style.font_weight = Some("bold".to_string());
|
style.font_weight = Some("bold".to_string());
|
||||||
style.margin = Edges { top: 21.0, bottom: 21.0, left: 0.0, right: 0.0 };
|
style.margin_relative[0] = Some(crate::Dimension::Em(0.67));
|
||||||
|
style.margin_relative[2] = Some(crate::Dimension::Em(0.67));
|
||||||
} else if tag == "h2" {
|
} else if tag == "h2" {
|
||||||
style.font_size = Some(24.0);
|
style.font_size = None;
|
||||||
|
style.font_size_raw = Some(crate::Dimension::Em(1.5));
|
||||||
style.font_weight = Some("bold".to_string());
|
style.font_weight = Some("bold".to_string());
|
||||||
style.margin = Edges { top: 19.0, bottom: 19.0, left: 0.0, right: 0.0 };
|
style.margin_relative[0] = Some(crate::Dimension::Em(0.83));
|
||||||
|
style.margin_relative[2] = Some(crate::Dimension::Em(0.83));
|
||||||
} else if tag == "h3" {
|
} else if tag == "h3" {
|
||||||
style.font_size = Some(18.7);
|
style.font_size = None;
|
||||||
|
style.font_size_raw = Some(crate::Dimension::Em(1.17));
|
||||||
style.font_weight = Some("bold".to_string());
|
style.font_weight = Some("bold".to_string());
|
||||||
style.margin = Edges { top: 18.0, bottom: 18.0, left: 0.0, right: 0.0 };
|
style.margin_relative[0] = Some(crate::Dimension::Em(1.0));
|
||||||
} else if tag == "h4" || tag == "h5" || tag == "h6" {
|
style.margin_relative[2] = Some(crate::Dimension::Em(1.0));
|
||||||
style.font_size = Some(16.0);
|
} else if tag == "h4" {
|
||||||
|
style.font_size = None;
|
||||||
|
style.font_size_raw = Some(crate::Dimension::Em(1.0));
|
||||||
style.font_weight = Some("bold".to_string());
|
style.font_weight = Some("bold".to_string());
|
||||||
style.margin = Edges { top: 21.0, bottom: 21.0, left: 0.0, right: 0.0 };
|
style.margin_relative[0] = Some(crate::Dimension::Em(1.33));
|
||||||
} else if matches!(tag, "p" | "ul" | "ol" | "menu" | "dir") {
|
style.margin_relative[2] = Some(crate::Dimension::Em(1.33));
|
||||||
style.margin = Edges { top: 16.0, bottom: 16.0, left: 0.0, right: 0.0 };
|
} else if tag == "h5" {
|
||||||
|
style.font_size = None;
|
||||||
|
style.font_size_raw = Some(crate::Dimension::Em(0.83));
|
||||||
|
style.font_weight = Some("bold".to_string());
|
||||||
|
style.margin_relative[0] = Some(crate::Dimension::Em(1.67));
|
||||||
|
style.margin_relative[2] = Some(crate::Dimension::Em(1.67));
|
||||||
|
} else if tag == "h6" {
|
||||||
|
style.font_size = None;
|
||||||
|
style.font_size_raw = Some(crate::Dimension::Em(0.67));
|
||||||
|
style.font_weight = Some("bold".to_string());
|
||||||
|
style.margin_relative[0] = Some(crate::Dimension::Em(2.33));
|
||||||
|
style.margin_relative[2] = Some(crate::Dimension::Em(2.33));
|
||||||
|
} else if matches!(tag, "p" | "dl" | "ul" | "ol" | "menu" | "dir") {
|
||||||
|
style.margin_relative[0] = Some(crate::Dimension::Em(1.0));
|
||||||
|
style.margin_relative[2] = Some(crate::Dimension::Em(1.0));
|
||||||
if matches!(tag, "ul" | "menu" | "dir") {
|
if matches!(tag, "ul" | "menu" | "dir") {
|
||||||
style.list_style = Some(crate::ListStyle::Disc);
|
style.list_style = Some(crate::ListStyle::Disc);
|
||||||
style.padding.left = 40.0;
|
style.padding.left = 40.0;
|
||||||
@@ -4307,10 +4328,31 @@ fn font_size_keyword(v: &str) -> Option<f32> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn apply_font_size(style: &mut LayoutStyle, value: &str) {
|
fn apply_font_size(style: &mut LayoutStyle, value: &str) {
|
||||||
if value.trim().contains('(') {
|
let value = value.trim();
|
||||||
|
match value.to_ascii_lowercase().as_str() {
|
||||||
|
// font-size is inherited; unset therefore has inherit semantics.
|
||||||
|
"inherit" | "unset" => {
|
||||||
|
style.font_size = None;
|
||||||
|
style.font_size_raw = None;
|
||||||
|
style.font_size_expression = None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
"initial" => {
|
||||||
|
style.font_size = Some(16.0);
|
||||||
|
style.font_size_raw = None;
|
||||||
|
style.font_size_expression = None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// The compact cascade does not retain origin/layer history. Preserve
|
||||||
|
// the already-applied lower-origin value instead of treating revert
|
||||||
|
// as inherit or initial.
|
||||||
|
"revert" | "revert-layer" => return,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
if value.contains('(') {
|
||||||
style.font_size = None;
|
style.font_size = None;
|
||||||
style.font_size_raw = None;
|
style.font_size_raw = None;
|
||||||
style.font_size_expression = Some(value.trim().to_string());
|
style.font_size_expression = Some(value.to_string());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
style.font_size_expression = None;
|
style.font_size_expression = None;
|
||||||
|
|||||||
Reference in new issue
Block a user