--------- (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)