I remember first learning about web technologies as a teenager. This was back in the days when the majority of websites still used tables for layout, and a “standards based” approach was just starting to gain popularity. The CSS Zen Garden was in vogue.
Back then, it was common that a large proportion of the time spent putting together a CSS-based website would be spent working around browser bugs. Internet Explorer was the main culprit – its dominance had allowed Microsoft to ignore web standards and interoperability. On the other side of the fence, Firefox was gaining popularity. It was a better browser than IE, with features like tabs and pop-up blocking, and Mozilla was much more committed to properly implementing web standards too. I remember being incredibly enthusiastic about Firefox and trying to encourage anybody who would listen to switch to it!
A few years later, Google first released Chrome and started eating away at the market share of other browsers. Whilst Google has generally behaved better than Microsoft did in the IE 5/6 era, we’re again in a situation where one browser maker is dominant and uses that power to influence the web .
I have to admit that for quite a few years Chrome had the edge over Firefox, particularly in terms of performance. But I held on to Firefox for for mainly ideological reasons. I wanted to support the independent candidate, and also thought it was important for developers in particular to use a diversity of browsers. As a Firefox user I’ve unfortunately seen plenty of breakage where developers clearly haven’t bothered to test extensively outside of Chrome.
I think that a world where the web is largely dominated by one commercial company, whose revenue comes almost entirely from advertising, is a scary prospect. So I was delighted when I read about Mozilla’s plans for Firefox Quantum , promising a great leap in performance. I was even more delighted when the promise became a reality. I’m once again proud to be a Firefox user.
All this got me quite inspired. Servo is written in Rust , which I had been interested in learning about anyway. And whilst Servo as a whole is a long way from being a production-ready browser, it is already showing results. So I decided to have a go at contributing to Servo.
My first step was to learn a bit more about Rust. I started reading The Rust Programming Language which is the “official” book about the language. After I had covered the basics I got impatient and wanted to find something concrete to work on.
I picked one off
which related to the implementation of the text control selection DOM API
. This API allows you to programmatically manipulate the selected text shown in an
element, and Servo’s implementation needed to be fleshed out.
My first pull request
was quite constrained in scope, but I quickly realised that there were large parts of this API which were unimplemented. There was also duplication (and subtle differences) between the implementations for
. So I set about fixing these things over quite a few additional PRs ( 2
There are contributing
and hacking quickstart
guides to get you going. On a normal Rust project, most operations are performed through the
utility, but Servo has its own
tool for doing almost everything (some
commands wrap around
$ ./mach build -d # creates a debug build of Servo $ ./mach run -d -- https://www.mozilla.org/ # runs the browser $ ./mach test-wpt [...] # runs some tests (see below)
Before submitting a pull request, you need to:
./mach test-tidyto check for compliance with Servo’s code style rules
There is also quite a keen focus on documentation, and I’ve often received pull request feedback asking me to add more documentation (I should learn from this really!)
If you want to change the reviewer (e.g. because you know it should be someone else), then you can comment with “r? @theirname” and the bot will dutifully comply.
Hopefully the reviewer will now provide you with some feedback. In my experience this has generally happened really quickly, although there have been some instances where I’ve needed to bug people in the IRC channel, including one PR which took about a month and a half to get merged.
Once the reviewer is happy, they’ll summon another bot with “ @bors-servo
r+”. This bot is an installation of bors
, and is in charge of actually merging the code. The bot has a queue of PRs to merge, and does so one at a time
, after running all the tests with the merged code. This is designed to ensure that it’s impossible for the
branch to contain failing tests, since the tests are always run on a commit before it becomes the new
Whilst some checks are run on your PR as soon as it is submitted, the full test suite is not run until @bors-servo tries to do the merge. Therefore, it is fairly common to see failures and have to make further updates.
I was interested to find out that most of Servo’s functionality is verified via the web platform tests suite, which I hadn’t heard of before. This is a cool project created by the W3C which aims to create a complete test suite for all the standardised web technologies. Thus any browser maker could run the suite to verify their conformance to the specifications.
There are a few different types of tests. The two main ones do the following:
Obviously there are loads of things which are currently broken or unimplemented in Servo. So as well as the tests, there is a bunch of metadata specifying which tests pass and which ones fail. When the builder runs the tests, it will refuse the merge if this metadata is out of sync with reality. So it checks that all the failing tests are actually marked failing, and all the passing tests are actually marked passing. This ensures that you notice if a change has caused some tests to start passing. (Running the full WPT test suite takes absolutely ages, so it is generally left for the builder to do rather than developers running it themselves.)
The Servo project is huge, and it takes a long time to build. One of the really cool things about Rust is how it provides a guarantee that certain classes of errors simply cannot occur in your programs. But this does mean that it has to do more work at compile time, which definitely shows when building something as large as Servo.
On my machine, building from scratch takes something like 45 minutes. After making a change, I don’t have to rebuild everything again, but I’m often still waiting for at least 3-5 minutes.
I have a relatively decent Dell XPS 9350 “ultrabook” laptop, but it’s really optimised for physical size rather than raw performance. Up until now I’ve found its processing power to be more than enough for the work I do, but now I’m building a really large project in a compiled language I am certainly seeing its limitations!
In particular, my laptop contains “only” 8GB of memory, which is non-upgradeable since it is soldered directly to the motherboard to reduce size. Sometimes when compiling, I’ll max out my memory and the kernel’s OOM killer
will step in and kill
. Fortunately this doesn’t seem to lose all the work that has been done, and I can usually successfully compile on the next attempt.
One thing that does help is
. This is equivalent to the
command in a normal Rust project. It checks that the code has no compilation errors, without actually building it. That’s much faster, and if I don’t actually need to run the browser or tests right now, then it provides a more pleasant feedback loop.
As already noted, the Servo codebase is really large. There is auto-generated documentation at doc.servo.org which can be a bit of an easier way to make sense of the many data structures and their relationships.
Generally, I am a puts debuggerer . But when the compile cycle is 3-5 minutes, you start to wonder whether there is a quicker way to figure out logical misunderstandings.
I have tried to use gdb a bit, with the help of this blog post . I can’t say it has been particularly successful. I’ve often managed to get the program paused at the correct location, but have struggled to inspect the state in any useful way. Most of the time I have something in a variable, and I can print out a representation of it, but I can’t manage to get GDB to call methods on it. In the end, if I manage to find out anything useful it generally seems to take longer than puts debuggering anyway. I think the tooling around debugging for Rust still has some way to go.
On the other hand, Servo itself has some really useful debugging tools built in which are accessed via the
option. You can see them by running
./mach run -d -- -Z help
There’s also the
environment variable which causes Rust to print out debug messages in the source code (i.e. those which use the
More recently I’ve been learning about other areas of the Servo codebase. One thing that I was particularly pleased with was
a change to where
markers are placed when there are adjacent floats
. This comes under the banner of “layout”, i.e. how Servo decides, based on your HTML and CSS, what should appear where on the page. It’s pretty complicated, at least for a first-timer, and it took me several days of reading code and specifications to submit that pull request.
I’m finding it really refreshing to work on things which are so challenging and new to me, so I hope that I can fix more layout bugs in the future! In general there are lots of complicated and interesting areas in a browser, so I’m excited to level up and dig into some of them in the future.
28 January 2018