Remove libcroco
I talked a bit to @verdre on IRC about my plans to remove libcroco and move to Rust-based CSS parser and matching engine. Librsvg uses Mozilla Servo's crates for CSS and it's working well.
This issue is more or less an RFC, and maybe we can use it to hold a history of the changes while libcroco is replaced.
Here is the very high-level view of what I want to do; plans of course may change when refactoring starts
- Move the libcroco sources directly under src/st; remove the libcroco dependency from the meson.build files.
- Start redoing the CSS micro-parsers in Rust, similar to how librsvg does it.
- Move to a Rust-based representation of CSS stylesheets.
- Do CSS matching and the cascade in Rust, with Servo's crates.
- Along the way, remove the bits of libcroco's sources that end up being unused.
I expect most of st-theme-node.c to move to Rust, at least the parsers, and the parts of st-theme.c that deal with matching.
Stylesheet parsing and storage
StTheme
uses cr_om_parser_simply_parse_buf()
to parse a complete CRStylesheet
and keeps it around in the collection of stylesheets.
Later, _st_theme_get_matched_properties()
implements matching. For each node, for each stylesheet, it gathers the CSS rules whose selectors match the node, sorts them by specificity, etc., and produces an array of CRDeclaration
.
That function gets called from ensure_properties()
in StThemeNode
. This is called whenever the Javascript code does themeNode.get_foo()
— the intention seems to be to do CSS matching lazily for each widget.
Every time a property is requested with themeNode.get_sometype("property_name")
or .lookup_sometype()
, the code calls ensure_properties()
, and then it walks the array of CRDeclaration
in node->properties
looking for one with the requested property_name
. It then parses the property value from the chain of CRTerm
tokens.
Some properties like the colors and border geometries cache their parsed values (e.g. they don't re-parse from the CRTerm
tokens on every lookup). I don't have profiling info or statistics to know if it's a problem that some properties are not cached, but we'll see.
These are very similar. From StTheme
and CRCascade
:
struct _StTheme
{
GObject parent;
...
GHashTable *stylesheets_by_file;
GHashTable *files_by_stylesheet;
CRCascade *cascade;
}
struct _CRCascadePriv {
CRStyleSheet *sheets[3]; /* one per origin */
...
}
From librsvg:
pub enum Origin {
UserAgent,
User,
Author,
}
pub struct Stylesheet {
origin: Origin,
qualified_rules: Vec<QualifiedRule>,
}
pub struct DocumentBuilder {
...
stylesheets: Vec<Stylesheet>,
}
StTheme
is a bit more dynamic with the mappings of stylesheet-by-file and the reverse; this shouldn't be a problem.
The micro-parsers
There is a parser for every property type (color, border, length, etc.). For example, get_length_from_term()
parses a length value.
While libcroco produces a linked list of long-lived CRTerm
, with one element per token, rust-cssparser produces short-lived Token
values from a Parser
. These are easy to make long-lived, if we wanted to keep around the "re-parse on every property lookup" scheme. I think we can gradually move to a scheme where properties done in C keep their current code flow, and properties done in Rust are parsed just once and kept that way.
These are very similar. From get_length_from_term()
:
static GetFromTermResult
get_length_from_term (StThemeNode *node,
CRTerm *term,
gboolean use_parent_font,
gdouble *length)
{
CRNum *num;
...
num = term->content.num;
switch (num->type)
{
case NUM_LENGTH_PX:
type = ABSOLUTE;
multiplier = 1 * scale_factor;
break;
case NUM_LENGTH_PT:
type = POINTS;
multiplier = 1;
break;
...
}
}
From librsvg:
impl Parse for Length {
fn parse(parser: &mut Parser) -> Result<Length, ValueErrorKind> {
let token = parser.next()?;
match *token {
Token::Number { value, .. } => {
Length::new(f64::from(finite_f32(value)?), LengthUnit::Px)
}
...
}
}
}
Build issues
I'm going to try to start a Rust-based sub-library inside st, and have Meson call "cargo build" for it. There are Not Too Gross Hacks(tm) in other GNOME modules that use Meson to call Cargo, and I hope to reuse them.
Location
My branch is https://gitlab.gnome.org/federico/gnome-shell/commits/remove-libcroco - I'll be putting this stuff there.