101 lines
4.5 KiB
Markdown
101 lines
4.5 KiB
Markdown
---------
|
|
|
|
(Title page)
|
|
Have you ever wanted to add your own crate to your favourite open source browser?
|
|
Or write your own Web API to your own custom browser fork?
|
|
My name is Ed Guloien and today I'm going to tell you how and what to watch out for.
|
|
|
|
(about://ed-guloien)
|
|
I'm a Senior Software Developer,
|
|
Formerly working on the Web Platform Networking team at Mozilla.
|
|
Currently at Quantum Bridge working on Distributed Symmetric Key Exchange
|
|
Interested in privacy, security and systems programming
|
|
|
|
(Interop 2025)
|
|
For the unfamiliar there is an initiative to improve web compatibility
|
|
by improving pre-selected Web API implementations across all browsers.
|
|
It's called Interop.
|
|
And a hundred years ago, for Interop 2025, I implemented the URLPattern Web API.
|
|
|
|
(URLPattern Web API)
|
|
URLPattern is basically pattern matching supporting ECMAScript Regex
|
|
for URLs and their components.
|
|
From Web JavaScript we construct a pattern and match strings against it.
|
|
|
|
(Denoland crate and Firefox)
|
|
Luckily for me there is an existing open source implementation by Denoland, written in rust.
|
|
So all I have to do is figure out how to incorporate it into Firefox.
|
|
This is Firefox's Fission Architecture,
|
|
it isolates web content from other web content, roughly by origin.
|
|
And we have the main process handling orchestration and networking.
|
|
We need URLPattern everywhere:
|
|
* for each content process
|
|
* and for Compression Dictionaries in the main process.
|
|
So we add it to the very common libXUL library within Firefox.
|
|
|
|
(WebAPI call flow to crate)
|
|
We fork Denoland's crate by vetting and vendoring it in to libXUL, inside gkrust.
|
|
And that compiles just fine.
|
|
But Firefox's Browser Engine, Gecko, is mostly C++.
|
|
* So we tell the build system about our FFI glue crate
|
|
* We use cbindgen to generate the headers required for compiling C++ against rust.
|
|
* Then generate JS-callable-C++-bindings from a webIDL file
|
|
* And implement the DOM wrapper that forwards the call to our glue crate.
|
|
|
|
(UrlPattern crate API)
|
|
Then we call the crate from our rust glue.
|
|
The crate API exposes a trait called RegExp and a few functions to go with it.
|
|
We simply implement trait, and call the functions to create patterns and match against them.
|
|
|
|
(Glue Crate)
|
|
Easy Peazy,
|
|
well,
|
|
not so fast.
|
|
|
|
(Reality Sets In)
|
|
Non-POD objects need to be passed-by-reference across the FFI.
|
|
* And since our common C ABI doesn't have references, cbindgen degrades our references to raw pointers.
|
|
* And if the object exposes internal types unknown to the other side you need an opaque pointer (a void pointer) to hide those details.
|
|
* This leads to unsafe, pointer casts, dereferences, return parameters and so on.
|
|
Similarly we use pointers to represent options, or write wrapper bindings to hide the gory details.
|
|
But sometimes we are lucky and can use specialized FFI types to that can handle the ownership model difference between C++ and Rust
|
|
|
|
(Pattern re-use)
|
|
Pattern construction is the most expensive part of using URLPattern.
|
|
We cannot feasibly do it on every match.
|
|
* So to save some cycles we pass the pattern object to the closest persistent object,
|
|
* all the way back in the generated DOM bindings,
|
|
* subjecting our pattern object to further marshaling across the ownership models.
|
|
But with that done, when the user requests a match or closes the page,
|
|
we pass the pattern to the rust crate for use or deletion.
|
|
|
|
(99.8%)
|
|
If you can get past all that,
|
|
You're pretty much there, with decent performance.
|
|
But if you want to go all the way you have to know
|
|
* that rust::regex is not EcmaRegex compliant
|
|
* and the urlpattern crate uses rust::regex by default.
|
|
Luckily for us, we're in Firefox,
|
|
we already HAVE an EcmaRegex implementation living in the SpiderMonkey Javascript engine
|
|
|
|
(Spidermonkey Challenges)
|
|
But it comes at the cost of toil.
|
|
We write similar FFI going the back to C++ to access SpiderMonkey.
|
|
But this API was not designed for C++ use.
|
|
* It requires JS context, which we lost touch with through the marshaling.
|
|
* It has a completely different memory model using Garbage Collection.
|
|
* A typical Web JS runtime can be interrupted to prioritize other JS.
|
|
* AND it uses UTF-16 by default, unlike Rust's UTF-8.
|
|
So we cast some spells:
|
|
* we spin up a JS context,
|
|
* we prevent Spidermonkey from interrupting it's execution,
|
|
* we root the objects to a context that will prevent the GC from marking them for deletion
|
|
* AND convert between UTF-8 and 16 as needed.
|
|
And if you do all that...
|
|
|
|
(Phew!)
|
|
Phew! All done.
|
|
Now you can add your own crates and WebAPI's
|
|
to an open source browser.
|
|
|
|
(references) |