fix(cssom): serialize computed mask from its longhands

This commit is contained in:
ldm0
2026-09-22 12:46:31 +08:00
parent 2ec1024152
commit 5c4da590df
2 changed files with 107 additions and 0 deletions
@@ -2394,6 +2394,9 @@ fn computed_style_property_value_after_style_update(
if property == "font-variant" {
return computed_font_variant_shorthand_value(runtime, handle, resolution);
}
if property == "mask" {
return computed_mask_shorthand_value(runtime, handle, resolution);
}
if property == "border" {
return computed_border_shorthand_value(runtime, handle, context);
}
@@ -2662,6 +2665,36 @@ fn computed_font_variant_shorthand_value(
serialize_font_variant_shorthand_values(&values).unwrap_or_default()
}
fn computed_mask_shorthand_value(
runtime: &JsContextHost,
handle: DomHandle,
resolution: StyleResolutionContext<'_>,
) -> String {
const LONGHANDS: [&str; 9] = [
"mask-mode",
"mask-repeat",
"mask-clip",
"mask-origin",
"mask-composite",
"mask-position-x",
"mask-position-y",
"mask-size",
"mask-image",
];
let mut block = moli_css_parse::CssDeclarationBlock::default();
for property in LONGHANDS {
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();
}
}
block.property_value("mask").unwrap_or_default()
}
fn computed_webkit_text_stroke_shorthand_value(
runtime: &JsContextHost,
handle: DomHandle,
@@ -12410,6 +12410,80 @@ fn svg_generic_presentation_attributes_apply_to_every_svg_element() {
);
}
#[test]
fn computed_mask_serializes_from_computed_longhands() {
let mut vm = new_parsed_test_vm(
"https://svg-mask-computed-shorthand.test/path/page.html",
r#"<!doctype html>
<style>
#stylesheet { mask: url(#stylesheet-mask); }
#layered {
mask: url(#layered-mask) center / contain no-repeat content-box exclude luminance;
}
#cascade { mask: url(#rule-mask); }
</style>
<svg id="svg">
<g id="stylesheet"></g>
<g id="attribute" mask="url(#attribute-mask)"></g>
<g id="layered"></g>
<g id="cascade" mask="url(#attribute-loses)"></g>
</svg>"#,
);
let result = vm
.eval(
r#"
(() => {
const namespace = 'http://www.w3.org/2000/svg';
const svg = document.getElementById('svg');
const value = element => getComputedStyle(element).getPropertyValue('mask');
const dynamic = document.createElementNS(namespace, 'unknown');
svg.append(dynamic);
const values = [
CSS.supports('mask', 'url(#supported)'),
value(dynamic),
value(document.getElementById('stylesheet')),
value(document.getElementById('attribute')),
value(document.getElementById('layered')),
value(document.getElementById('cascade')),
];
dynamic.setAttribute('mask', 'url(#dynamic-mask)');
values.push(value(dynamic));
dynamic.removeAttribute('mask');
values.push(value(dynamic));
dynamic.style.mask =
'url(#inline-mask) center / contain no-repeat content-box exclude luminance';
values.push(value(dynamic));
dynamic.style.mask = 'none';
dynamic.style.maskImage = 'url(#longhand-mask)';
dynamic.style.maskMode = 'luminance';
values.push(value(dynamic));
return values.join('|');
})()
"#,
)
.expect("computed mask shorthand should evaluate");
assert_eq!(
result,
concat!(
"true|none|",
"url(\"https://svg-mask-computed-shorthand.test/path/page.html#stylesheet-mask\")|",
"url(\"https://svg-mask-computed-shorthand.test/path/page.html#attribute-mask\")|",
"url(\"https://svg-mask-computed-shorthand.test/path/page.html#layered-mask\") ",
"50% 50% / contain no-repeat content-box exclude luminance|",
"url(\"https://svg-mask-computed-shorthand.test/path/page.html#rule-mask\")|",
"url(\"https://svg-mask-computed-shorthand.test/path/page.html#dynamic-mask\")|none|",
"url(\"https://svg-mask-computed-shorthand.test/path/page.html#inline-mask\") ",
"50% 50% / contain no-repeat content-box exclude luminance|",
"url(\"https://svg-mask-computed-shorthand.test/path/page.html#longhand-mask\") luminance",
)
);
}
#[test]
fn svg_special_presentation_attributes_follow_element_scopes_and_cascade() {
let mut vm = new_parsed_test_vm(