Gnome-shell uses CSS processing code that dates from HippoCanvas, a CSS-aware canvas from around 2006. It uses libcroco to parse CSS, and implements selector matching by hand in C.
This code is getting rather dated, and libcroco is unmaintained.
I've been reading the code for
StTheme
and
StThemeNode
,
and it looks very feasible to port it gradually to Rust, by using the
same crates that librsvg uses, and eventually removing libcroco
altogether: gnome-shell is the last module that uses libcroco in
distro packages.
Strategy
StTheme
and StThemeNode
use libcroco to load CSS stylesheets and
keep them in memory. The values of individual properties are just
tokenized and kept around as a linked list of CRTerm
; this struct
represents a single token.
Later, the drawing code uses functions like
st_theme_node_lookup_color(node, "property_name")
or
st_theme_node_lookup_length()
to query the various properties that
it needs. It is then that the type of each property gets
determined: prior to that step, property values are just tokenized,
not parsed into usable values.
I am going to start by porting the individual parsers to Rust, similar to what Paolo and I did for librsvg. It turns out that there's some code we can share.
So far I have the parser for colors implemented in Rust. This removes a little bunch of code from the C parsers, and replaces it with a little Rust code, since the cssparser crate can already parse CSS colors with alpha with no extra work — libcroco didn't support alpha.
As a bonus, this supports hsl()
colors in addition to rgb()
ones
out of the box!
After all the parsers are done, the next step would be to convert the representation of complete stylesheets into pure Rust code.
What can we expect?
A well-maintained CSS stack. Firefox and Servo both use the crates in question, so librsvg and gnome-shell should get maintenance of a robust CSS stack "for free", for the foreseeable future.
Speed. Caveat: I have no profile data for gnome-shell yet, so I don't know how much time it spends doing CSS parsing and cascading, but it looks like the Rust version has a good chance of being more efficient.
The selectors crate has some very interesting optimizations from Mozilla Servo, and it is also now used in Firefox. It supports doing selector matching using Bloom filters, and can also avoid re-cascading child nodes if a change to a parent would not cause its children to change.
All the parsing is done with zero-copy parsers thanks to Rust's string
slices; without so many malloc()
calls in the parsing code path,
the parsing stage should really fly.
More CSS features. The selectors crate can do matching on
basically all kinds of selectors as defined by recent CSS specs; one
just has to provide the correct hooks into the calling code's
representation of the DOM tree. The kind of matching that StTheme
can do is somewhat limited; the rustification should make it match
much more closely to what people expect from CSS engines in web
browsers.
A well-defined model of property inheritance. StThemeNode
's
model for CSS property inheritance is a bit ad-hoc and inconsistent.
I haven't quite tested it, but from looking at the code, it seems that
not all properties get inherited in the same way. I hope to move it
to something closer to what librsvg already does, which should make it
match people's expectations from the web.
In the meantime
I have a merge request ready to simply move the libcroco source code directly inside gnome-shell's source tree. This should let distros remove their libcroco package as soon as possible. That MR does not require Rust yet.
My playground is here:
- Gnome-shell branch to rustify the styles
- Stylish, a Rust library that will implement gnome-shell's styling code.
This does not compile yet! I'll plug things together tomorrow.
(Oh, yes, the project to redo Firefox's CSS stack in Rust used to be called Stylo. I'm calling this Stylish, as in Styles for the Shell.)