100 lines
4.1 KiB
Markdown
100 lines
4.1 KiB
Markdown
---------
|
|
|
|
(Title page)
|
|
Have you ever wanted to add your own crate
|
|
Or write your own Web API to your favourite browser?
|
|
My name is Ed Guloien and today
|
|
I'm going to tell you how.
|
|
|
|
(about://ed-guloien)
|
|
I'm a Senior Software Developer,
|
|
Formerly working on the Web Platform Networking team at Mozilla.
|
|
Currently writing rust full time Quantum Bridge working on Distributed Symmetric Key Exchange.
|
|
|
|
(Interop 2025)
|
|
There is an initiative to improve web compatibility by improving Web API implementations
|
|
across the major browsers.
|
|
It's called Interop and it happens every year.
|
|
And a hundred years ago, for Interop 2025,
|
|
I implemented the URLPattern Web API.
|
|
|
|
(URLPattern Web API)
|
|
URLPattern is basically pattern matching for URLs and their components.
|
|
We can construct a pattern object and match strings against it.
|
|
|
|
(Denoland crate and Firefox)
|
|
Luckily for us there is an existing open source implementation by Denoland, written in rust.
|
|
So all we 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.
|
|
We need URLPattern everywhere:
|
|
* for each web content process
|
|
* and for Compression Dictionaries in the main process.
|
|
So we add it to libXUL, a common library within Firefox.
|
|
|
|
(WebAPI call flow to crate)
|
|
We fork Denoland's crate by vetting, vendoring and telling the build system to compile it.
|
|
This works just ffine.
|
|
But Firefox's Browser Engine is mostly C++ and we're missing a call path.
|
|
* So we generate JS_callable C++ bindings from a webIDL file
|
|
* Implement a DOM wrapper that will forward the calls to our gloue crate
|
|
* And Tell the build system about our FFI glue crate
|
|
* It uses cbindgen to generate headers for C++ compilation against rust
|
|
* And include a C++ convenience wrapper and make our rust call the crate
|
|
|
|
(UrlPattern crate API)
|
|
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, right?
|
|
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 to hide those details.
|
|
* This leads to all sorts of unsafe pointer voodoo
|
|
|
|
Similarly, to represent options we use pointers, or write wrapper bindings to hide the gory details.
|
|
But sometimes we get lucky and find a specialized FFI type that can handle the ownership model difference between C++ and Rust
|
|
|
|
(Pattern re-use)
|
|
We get that working and quickly notice that
|
|
Pattern construction is the most expensive part of using URLPattern.
|
|
So 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,
|
|
* and write the additional marshaling to do it.
|
|
With that done, when the user requests a match: we pass the pattern along to the crate.
|
|
To avoid memory leaks, when the DOM binding goes out of scope the pattern is destroyed by it's rust creator.
|
|
|
|
(99.8%)
|
|
If you get this far
|
|
You're pretty much there, with decent performance.
|
|
But if you want the whole banana, 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 in SpiderMonkey, the 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 through all the marshaling.
|
|
* It uses Garbage Collection.
|
|
* Calling JS can be interrupted to prioritize other JS.
|
|
So we cast some spells:
|
|
* we spin up a JS context,
|
|
* we prevent Spidermonkey from interrupting execution,
|
|
* we root the objects to a context that is safe from GC
|
|
* AND convert between UTF-8 and 16 as needed.
|
|
|
|
And if you do all that...
|
|
|
|
(Phew!)
|
|
You're done.
|
|
Now you can add your own crates and Web API's to your favourite open source browser.
|