mirror of
https://github.com/lexmount/moli.git
synced 2026-11-14 08:07:52 +00:00
Move Taffy to 0.15 and Stylo to 0.22 using fixed fork revisions, with the
matching stylo_taffy adapter. Upgrade cssparser and the production HTML/XML
parsers while preserving the vendored XML behavior.
Adapt sizing inputs, baselines, grid positions, overflow and out-of-flow
ownership. Preserve font-stretch CSSOM compatibility and intrinsic sizing
through nested atomic inline probes.
Validation: root cargo fmt --all; strict workspace/all-target/all-feature
Clippy; Nextest 20280 passed, 18 skipped; Release build; five browser
regression rounds; RTL and nested intrinsic Chromium geometry controls.
Full WPT Cross is recorded separately with its Cases refresh.
Original-commit: 281a920d89
131 lines
4.2 KiB
Rust
131 lines
4.2 KiB
Rust
use cssparser::{Parser, Token};
|
|
|
|
pub fn normalize_root_margin(value: &str) -> Option<String> {
|
|
let input = value;
|
|
let mut parser = Parser::new(input);
|
|
let raw_parts = parser.parse_entirely(parse_root_margin_components).ok()?;
|
|
let parts: Vec<&str> = match raw_parts.as_slice() {
|
|
[] => return Some("0px 0px 0px 0px".to_owned()),
|
|
[a] => vec![a.as_str(), a.as_str(), a.as_str(), a.as_str()],
|
|
[a, b] => vec![a.as_str(), b.as_str(), a.as_str(), b.as_str()],
|
|
[a, b, c] => vec![a.as_str(), b.as_str(), c.as_str(), b.as_str()],
|
|
[a, b, c, d] => vec![a.as_str(), b.as_str(), c.as_str(), d.as_str()],
|
|
_ => return None,
|
|
};
|
|
Some(parts.join(" "))
|
|
}
|
|
|
|
pub fn root_margin_components(value: &str, root_width: f64) -> [f64; 4] {
|
|
let mut result = [0.0; 4];
|
|
for (index, part) in value.split_ascii_whitespace().take(4).enumerate() {
|
|
result[index] = if let Some(number) = part.strip_suffix("px") {
|
|
number.parse::<f64>().unwrap_or(0.0)
|
|
} else if let Some(number) = part.strip_suffix('%') {
|
|
number
|
|
.parse::<f64>()
|
|
.map(|percentage| (percentage / 100.0) * root_width)
|
|
.unwrap_or(0.0)
|
|
} else {
|
|
0.0
|
|
};
|
|
}
|
|
result
|
|
}
|
|
|
|
fn parse_root_margin_components<'i>(
|
|
input: &mut Parser<'i>,
|
|
) -> Result<Vec<String>, cssparser::ParseError<()>> {
|
|
let mut parts = Vec::new();
|
|
while !input.is_exhausted() {
|
|
if parts.len() == 4 {
|
|
return Err(cssparser::ParseError::custom(()));
|
|
}
|
|
parts.push(parse_root_margin_component(input)?);
|
|
}
|
|
Ok(parts)
|
|
}
|
|
|
|
fn parse_root_margin_component<'i>(
|
|
input: &mut Parser<'i>,
|
|
) -> Result<String, cssparser::ParseError<()>> {
|
|
match input.next()?.clone() {
|
|
Token::Dimension { value, unit, .. } if unit.eq_ignore_ascii_case("px") => {
|
|
format_css_number(value)
|
|
.map(|number| format!("{number}px"))
|
|
.ok_or_else(|| cssparser::ParseError::custom(()))
|
|
}
|
|
Token::Percentage { unit_value, .. } => format_css_number(unit_value * 100.0)
|
|
.map(|number| format!("{number}%"))
|
|
.ok_or_else(|| cssparser::ParseError::custom(())),
|
|
_ => Err(cssparser::ParseError::custom(())),
|
|
}
|
|
}
|
|
|
|
fn format_css_number(value: f32) -> Option<String> {
|
|
if !value.is_finite() {
|
|
return None;
|
|
}
|
|
if value == 0.0 {
|
|
return Some("0".to_owned());
|
|
}
|
|
let mut text = value.to_string();
|
|
if let Some(dot) = text.find('.') {
|
|
while text.ends_with('0') {
|
|
text.pop();
|
|
}
|
|
if text.len() == dot + 1 {
|
|
text.pop();
|
|
}
|
|
}
|
|
Some(text)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::{normalize_root_margin, root_margin_components};
|
|
|
|
#[test]
|
|
fn parser_uses_cssparser_tokens() {
|
|
for value in ["", " \r\n\t\u{c}", "/**/", "/* unclosed", " /**/ \n /**/ "] {
|
|
assert_eq!(
|
|
normalize_root_margin(value).as_deref(),
|
|
Some("0px 0px 0px 0px"),
|
|
"{value:?}",
|
|
);
|
|
}
|
|
assert_eq!(
|
|
normalize_root_margin("10px /*comment*/ 5% 0px -2.5px").as_deref(),
|
|
Some("10px 5% 0px -2.5px")
|
|
);
|
|
assert_eq!(
|
|
normalize_root_margin("\n1PX\t2px").as_deref(),
|
|
Some("1px 2px 1px 2px")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parser_rejects_non_margin_tokens() {
|
|
for value in [
|
|
"0", "+0", "-0", "0.0", "0e0", "1e-50", "1px 0", "0% 0 1px", "\u{a0}", "\u{2003}",
|
|
"\u{b}", "\u{85}", "\u{2028}", "\u{2029}", "()", "[]", "{}", ")", "]", "}",
|
|
] {
|
|
assert_eq!(normalize_root_margin(value), None, "{value:?}");
|
|
}
|
|
assert_eq!(normalize_root_margin("calc(1px)").as_deref(), None);
|
|
assert_eq!(normalize_root_margin("1px, 2px").as_deref(), None);
|
|
assert_eq!(normalize_root_margin("1em").as_deref(), None);
|
|
assert_eq!(
|
|
normalize_root_margin("1px 2px 3px 4px 5px").as_deref(),
|
|
None
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn components_resolve_percentages_against_root_width() {
|
|
assert_eq!(
|
|
root_margin_components("10px 5% 0px -2.5px", 200.0),
|
|
[10.0, 10.0, 0.0, -2.5]
|
|
);
|
|
}
|
|
}
|