Skip to content

Commit 5e07b1d

Browse files
feat(csscascade,websem): admit the font-family hint, refuse text-anchor in CSS
The text rung's cascade pair, both measured against Chromium 149 first. font-family joins the admitted presentation-hint set: the attribute alone selects the face, an author rule beats it, font-family="" drops like an invalid declaration, and the property inherits — each pinned by the precedence law. text-anchor joins the not-represented refusal list. Chromium consumes it from the cascade (a rule anchors an attribute-free <text>, and text-anchor: end beats text-anchor="middle"), but the property is engine = "gecko" at the Stylo pin, so the servo build has no such longhand and a sheet declaring one was a silent drop. Same shape as the gradient rung's stop-color row, one rung later. The attribute spelling stays admitted and is read by the text compiler.
1 parent 6ce9851 commit 5e07b1d

3 files changed

Lines changed: 39 additions & 0 deletions

File tree

‎crates/csscascade/src/dom.rs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,13 @@ fn admitted_svg_presentation_property(local: &str) -> Option<LonghandId> {
729729
// (measured: `<g font-size="32">` makes a `0.5em` stroke 16px), so
730730
// dropping it here computed the wrong width from the right document.
731731
"font-size" => Some(LonghandId::FontSize),
732+
// The text rung's addition: the family a run resolves against.
733+
// Chromium treats it as a presentation attribute (measured: the
734+
// attribute alone selects the face, an author rule beats it, and
735+
// `font-family=""` drops so the element renders in the default
736+
// family). The consumer matches the computed family against its
737+
// declared font environment — a name is not a font identity.
738+
"font-family" => Some(LonghandId::FontFamily),
732739
// The visibility rung's pair. Both are SVG2 presentation attributes
733740
// whose author-rule precedence is measured (a stylesheet
734741
// `visibility: visible` overrides `visibility="hidden"` in

‎crates/csscascade/tests/svg_presentation_hints.rs‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const STANDALONE: &str = r##"<svg xmlns="http://www.w3.org/2000/svg" width="64"
2525
#transform-none-beats-hint { transform: none; }
2626
#transform-style-attr-beats-rule { transform: translate(30px, 0px); }
2727
#transform-webkit-alias { -webkit-transform: translate(30px, 0px); }
28+
#family-rule-beats-hint { font-family: monospace; }
2829
</style>
2930
<rect id="hint-only" fill="#16a34a" width="8" height="8"/>
3031
<rect id="named" fill="rebeccapurple" width="8" height="8"/>
@@ -53,6 +54,10 @@ const STANDALONE: &str = r##"<svg xmlns="http://www.w3.org/2000/svg" width="64"
5354
<rect id="transform-run-together" transform="translate(10-10)" width="8" height="8"/>
5455
<rect id="transform-webkit-alias" width="8" height="8"/>
5556
<g color="#d0342c"><rect id="color-basis" fill="currentColor" width="8" height="8"/></g>
57+
<text id="family-hint" font-family="Ahem">X</text>
58+
<text id="family-rule-beats-hint" font-family="Ahem">X</text>
59+
<text id="family-invalid-hint" font-family="">X</text>
60+
<g font-family="Ahem"><text id="family-inherited">X</text></g>
5661
<linearGradient id="gradient-transform-hint" gradientTransform="translate(10 10)"/>
5762
<linearGradient id="gradient-plain-transform-inert" transform="translate(10 10)"/>
5863
</svg>"##;
@@ -112,6 +117,25 @@ fn standalone_svg_presentation_hints_enter_below_author_rules() {
112117
"1",
113118
"unadmitted presentation attributes must not leak into the cascade"
114119
);
120+
// The text rung's hint: `font-family` is what a run resolves against.
121+
// Measured in Chromium: the attribute alone selects the face, an author
122+
// rule beats it, `font-family=""` drops to the default family, and the
123+
// property inherits.
124+
assert_eq!(property(root, "family-hint", LonghandId::FontFamily), "Ahem");
125+
assert_eq!(
126+
property(root, "family-rule-beats-hint", LonghandId::FontFamily),
127+
"monospace"
128+
);
129+
assert_ne!(
130+
property(root, "family-invalid-hint", LonghandId::FontFamily),
131+
"Ahem",
132+
"an invalid family hint drops like an invalid CSS declaration"
133+
);
134+
assert_eq!(
135+
property(root, "family-inherited", LonghandId::FontFamily),
136+
"Ahem",
137+
"font-family inherits from the group carrying the hint"
138+
);
115139
// `font-size` is admitted for its *basis*, not because anything paints it:
116140
// it is what an `em` length resolves against, and Chromium treats it as a
117141
// presentation attribute. Inherited, so the shape reads the group's.

‎crates/websem/src/svg.rs‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,14 @@ const CASCADE_PROPERTIES_NOT_REPRESENTED: &[&str] = &[
10651065
"stop-color",
10661066
"stop-opacity",
10671067
"color-interpolation",
1068+
// The text rung's row, the same shape one rung later: Chromium consumes
1069+
// `text-anchor` from the cascade (measured: a rule anchors an
1070+
// attribute-free `<text>`, and `text-anchor: end` beats
1071+
// `text-anchor="middle"`), but the property is `engine = "gecko"` at the
1072+
// Stylo pin, so the servo build has no such longhand and a sheet
1073+
// declaring one is a silent drop. The *attribute* spelling is admitted
1074+
// and read directly by the text compiler.
1075+
"text-anchor",
10681076
// CSS motion path: measured to translate and rotate an SVG shape — and a
10691077
// whole `<g>` subtree — off its authored position.
10701078
"offset",

0 commit comments

Comments
 (0)