fix(cssom): serialize computed font from native longhands

Serialize computed font from the retained style read and Stylo font longhand metadata. Reuse the native font serializer for CSS2 variant/stretch constraints, reset-only longhands and omission of initial components. Resolve font size, weight and line height through the shared CSSOM path.

Keep computed Typed OM font values opaque, property-associated and immutable while retained maps observe new styles. Cover relative and inherited sizes, numeric line height, snapshots and supported unrepresentable longhands.
This commit is contained in:
ldm0
2026-10-23 04:26:49 +08:00
committed by Donough Liu
parent 700236f029
commit f26501e0ba
5 changed files with 84 additions and 0 deletions
@@ -1,6 +1,7 @@
use super::*;
mod background;
mod font;
pub(in crate::native_bridge::element::styles) fn style_property_value_with_context(
runtime: &JsContextHost,
@@ -104,6 +105,9 @@ pub(super) fn computed_style_property_value_after_style_update(
if property == "text-emphasis" {
return computed_text_emphasis_shorthand_value(runtime, handle, context);
}
if property == "font" {
return font::computed_shorthand(runtime, handle, resolution);
}
if property == "font-variant" {
return computed_font_variant_shorthand_value(runtime, handle, resolution);
}
@@ -0,0 +1,27 @@
use super::*;
pub(super) fn computed_shorthand(
runtime: &JsContextHost,
handle: DomHandle,
resolution: StyleResolutionContext<'_>,
) -> String {
// Servo's font schema excludes this compatibility longhand. It is reset
// by font, but a non-initial value cannot be expressed by the shorthand.
if resolution.computed_property(runtime, handle, "font-size-adjust") != "none" {
return String::new();
}
let mut block = moli_css_parse::CssDeclarationBlock::default();
for longhand in style::properties::ShorthandId::Font.longhands() {
let property = longhand.name();
let value = resolution.computed_property(runtime, handle, property);
if value.is_empty()
|| block.set_property(property, &value, false)
== moli_css_parse::CssSetResult::ParseError
{
return String::new();
}
}
// The native serializer handles CSS2 variant/stretch restrictions,
// reset-only longhands and omission of initial components.
block.property_value("font").unwrap_or_default()
}
@@ -59,6 +59,7 @@ mod computed_style_access;
mod content_and_invalidation;
mod cross_document_and_animations;
mod css_computed_background;
mod css_computed_font;
mod css_computed_map_enumeration;
mod css_variable_fonts;
mod nested_document_invalidation;
@@ -0,0 +1,42 @@
(() => {
const check = (ok, message) => {if (!ok) throw new Error(message);};
const cases = [
['font:16px serif', '16px serif'],
['font:italic bold 20px / 1.5 monospace', 'italic 700 20px / 30px monospace'],
['font:small-caps bold condensed 24px / 2 serif', 'small-caps 700 condensed 24px / 48px serif'],
['font:oblique 20deg 20px serif', 'oblique 20deg 20px serif'],
['font-size:24px;font-family:serif;font-weight:700;line-height:1.25', '700 24px / 30px serif'],
];
for (const [text, expected] of cases) {
const element = document.createElement('div');
element.style.cssText = text;
document.body.append(element);
const computed = getComputedStyle(element);
const value = computed.font;
check(getComputedStyle(element).font === expected, text + ': ' + getComputedStyle(element).font);
check(value === expected, text + ': serialized value');
element.style.font = '12px sans-serif';
check(value === expected, 'retained font string');
check(computed.font === '12px sans-serif', 'live computed declaration');
element.remove();
}
const element = document.createElement('div');
document.body.append(element);
for (const [property, value] of [
['font-variant-ligatures', 'none'],
['font-variant-east-asian', 'full-width'],
['font-kerning', 'normal'],
]) {
element.style.cssText = 'font:16px serif';
element.style.setProperty(property, value);
check(element.style.font === '', property + ': specified font cannot represent value');
check(getComputedStyle(element).font === '', property + ': computed font cannot represent value');
}
const parent = document.createElement('div');
parent.style.font = 'bold 20px / 1.5 monospace';
parent.append(element); document.body.append(parent);
element.style.cssText = 'font-size:2em';
check(getComputedStyle(element).font === '700 40px / 60px monospace', 'inherited computed longhands');
parent.remove();
return true;
})()
@@ -0,0 +1,10 @@
use super::*;
#[test]
fn css_computed_font_serializes_resolved_longhands_and_retains_values() {
let mut vm = new_parsed_test_vm("https://computed-font.test/", "<!doctype html><body>");
assert_eq!(
vm.eval(include_str!("css_computed_font.js")).unwrap(),
"true"
);
}