Skip to content

Commit

Permalink
Extract CSS legacy name aliases defined in specs
Browse files Browse the repository at this point in the history
CSS specs sometimes define property names that are "legacy name aliases" of
another property name. Reffy extracted the legacy property names (during
post-processing) as they are defined in a `<dfn>` element but did not flag
them in any way.

The crawler now adds a `legacyAliasOf` key to the property in the CSS extract
whose value is the name of the aliased property.

Extraction is based on looking for links to the term "legacy name alias"
(defined in CSS Cascade 4 and 5) and simple patterns around it. Either:
1. a dfn for a property followed by a reference to the aliased property; or
2. a table with two columns: dfns in the first column, references to the
aliased properties in the second column.

First case handles legacy definitions in `css-align-3`, `css-fonts-4`,
and `css-ui-4`. Second case handles legacy definitions in `compat`.

Legacy definitions in `css-text-3` and `css-text-4` are not extracted... and
that is a good thing because they define `word-wrap` both as an actual property
and as a legacy name alias of `overflow-wrap`, which seems wrong.

Legacy definitions in `css-flexbox-1` are not extracted either... which also
is a good thing given that `compat` handles them already!

The `css-ui-4` spec also defines `-webkit-user-select` as an alias of
`user-select`, but the spec does not use the "legacy name alias" mechanism,
apparently on purpose:
https://drafts.csswg.org/css-ui-4/#propdef--webkit-user-select
That relationship will have to be added with a patch if we really want it.

Fixes #538 (although note there will remain a few CSS properties in extracts
that do not have a real "value") and w3c/webref#1427
  • Loading branch information
tidoust committed Jan 20, 2025
1 parent b613052 commit b9de52a
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 7 deletions.
1 change: 1 addition & 0 deletions schemas/browserlib/extract-cssdfn.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"value": { "$ref": "../common.json#/$defs/cssValue" },
"newValues": { "$ref": "../common.json#/$defs/cssValue" },
"values": { "$ref": "../common.json#/$defs/cssValues" },
"legacyAliasOf": { "$ref": "../common.json#/$defs/cssPropertyName" },
"styleDeclaration": {
"type": "array",
"items": { "type": "string" },
Expand Down
71 changes: 64 additions & 7 deletions src/browserlib/extract-cssdfn.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ export default function () {
const warnings = [];

const res = {
// Properties are always defined in dedicated tables in modern CSS specs
properties: extractDfns({
selector: 'table.propdef:not(.attrdef)',
extractor: extractTableDfns,
duplicates: 'merge',
warnings
}),
// Properties are always defined in dedicated tables in modern CSS specs,
// Legacy properties are always defined in prose in a dfn with a nearby
// reference to "legacy name alias"
properties: []
.concat(extractDfns({
selector: 'table.propdef:not(.attrdef)',
extractor: extractTableDfns,
duplicates: 'merge',
warnings
}))
.concat(extractLegacyProperties(document)),

// At-rules, selectors, functions and types are defined through dfns with
// the right "data-dfn-type" attribute
Expand Down Expand Up @@ -826,3 +830,56 @@ const extractProductionRules = root => {

return rules;
}


/**
* Extract legacy alias relationships, looking for occurrences of the term
* "legacy name alias".
*
* Next to it, there should be:
* 1. a dfn for a property followed by a reference to the aliased property; or
* 2. a table with two columns: dfns in the first column, references to the
* aliased properties in the second column.
*/
const extractLegacyProperties = doc =>
[...doc.querySelectorAll('a[href*="#legacy-name-alias"]')]
.map(el => el.parentElement)
.map(el => {
const dfn = el.querySelector('dfn[data-dfn-type="property"]');
const alias = el.querySelector('a[data-link-type="property"]');
if (dfn && alias) {
// Aliasing is defined in prose
return {
name: normalize(dfn.textContent),
href: getAbsoluteUrl(dfn),
legacyAliasOf: normalize(alias.textContent)
};
}
else {
// Look for a compat table right after the paragraph
const table = el.nextElementSibling;
if (!table || table.nodeName !== 'TABLE') {
return null;
}
if ([...table.querySelectorAll('thead > tr > th')].length !== 2) {
return null;
}
return [...table.querySelectorAll('tbody > tr')]
.map(row => {
const dfn = row.querySelector('dfn[data-dfn-type="property"]');
const alias = row.querySelector('a[data-link-type="property"]');
if (dfn && alias) {
return {
name: normalize(dfn.textContent),
href: getAbsoluteUrl(dfn),
legacyAliasOf: normalize(alias.textContent)
};
}
else {
return null;
}
});
}
})
.flat()
.filter(prop => !!prop);
63 changes: 63 additions & 0 deletions tests/extract-css.js
Original file line number Diff line number Diff line change
Expand Up @@ -1580,6 +1580,69 @@ that spans multiple lines */
"value": "currentColor | <color> [<icccolor>] | inherit",
"initial": "white"
}]
},

{
title: 'extracts a legacy name alias',
html: `<p>
The <dfn data-dfn-type="property" id="old">good-old</dfn>
property is a <a href="blah#legacy-name-alias">legacy name alias</a> of
the <a data-link-type="property">brand-new</a> property.
</p>
`,
css: [{
name: 'good-old',
href: 'about:blank#old',
legacyAliasOf: 'brand-new'
}]
},

{
title: 'extracts legacy name aliases from a suitable table',
html: `<p>
The following properties must be supported as
<a href="foo#legacy-name-alias">legacy name aliases</a> of the
corresponding unprefixed property:
</p>
<table>
<thead>
<tr><th>First column</th><th>Second column</th></tr>
</thead>
<tbody>
<tr>
<td><dfn data-dfn-type="property" id="old">good-old</dfn></td>
<td><a data-link-type="property">brand-new</a></td>
</tr>
<tr>
<td><dfn data-dfn-type="property" id="webkit">-webkit-old</dfn></td>
<td><a data-link-type="property">brand-new</a></td>
</tr>
</tbody>
</table>
`,
css: [
{
name: 'good-old',
href: 'about:blank#old',
legacyAliasOf: 'brand-new'
},
{
name: '-webkit-old',
href: 'about:blank#webkit',
legacyAliasOf: 'brand-new'
}
]
},

{
title: 'avoids legacy name alias references in prose without a dfn',
html: `<p>
The <a data-link-type="property" id="old">good-old</a>
property is a <a href="blah#legacy-name-alias">legacy name alias</a> of
the <a data-link-type="property">brand-new</a> property.
</p>
`,
css: []
}
];

Expand Down

0 comments on commit b9de52a

Please sign in to comment.