Looking For

Firefox Tooling Announcements

PerfCompare deployed (Oct 21st, 2026)

The Rust Programming Language Blog

Announcing rustup 1.29.2

The rustup team is happy to announce the release of rustup version 1.29.2.

Rustup is the recommended tool to install Rust, a programming language that empowers everyone to build reliable and efficient software.

What's new in rustup 1.29.2

This is a small release which only updates the rustls-webpki and webpki-root-certs dependencies to address the problematic eMudhra intermediate certificates issue described in CVE-2026-111943.

See also RUSTSEC-2026-0382.

Further details are available in the changelog!

How to update

If you have a previous version of rustup installed, getting the new one is as easy as stopping any programs which may be using rustup (e.g. closing your IDE) and running:

$ rustup self update

Rustup will also automatically update itself at the end of a normal toolchain update:

$ rustup update

If you don't have it already, you can get rustup from the appropriate page on our website.

Rustup's documentation is also available in the rustup book.

Caveats

Rustup releases can come with problems not caused by rustup itself but just due to having a new release.

In particular, anti-malware scanners might block rustup or stop it from creating or copying files, especially when installing rust-docs which contains many small files.

Issues like this should be automatically resolved in a few weeks when the anti-malware scanners are updated to be aware of the new rustup release.

Thanks

Thanks again to all the contributors who made this rustup release possible!

Chris H-C

Eleven-Year Moziversary

I’ve been working at Mozilla for eleven years, my golly. I’m still on my third hardware refresh (a Dell XPS I continue to dislike), I’m up to my fourth CEO, and 76 reorgs have happened (well, I know that quite a few things that would normally have counted haven’t been counted there, so it’s probably a higher number than that).

I’m still getting Firefox Desktop to use Glean instead of Legacy Telemetry. We’re more than two-thirds of the way done with removing the legacy system, with concrete plans about what to do with the rest. I’m still not blogging as much as I’d like, but I have some honest hope that I might change that in no small part thanks to someone who mentioned in passing that they liked my posts.

Looking back at last year’s predictions, I did indeed requisition a second work monitor, which is working out well.

I was right that FOG Migration hasn’t completely removed all of Legacy Telemetry, though we’re a lot closer than I thought we’d be. I do have a little time to think about doing other things, but not all that much, so the part of the prediction where I’d do something else is a “maybe”.

My desire to push for a sensible user population measurement did not have a chance to be acted upon. We might be shifting what the business counts, but not to anything I’ve had a say in. At least one of the reorgs that happened changed the organizational distance between my team and the other parts of Data that would be involved in such a measurement, other non-work things took way too much time and resulted in slower overall work velocity, and FOG Migration kept taking priority.

I didn’t think there’d be an All Hands, and in the off-change there were, it’d be a multi-city “Summit style” affair. I was half wrong twice: there wasn’t an All Hands but there was indeed an all-Engineering Summit; it wasn’t in multiple cities but just one. I was right that Toronto finally got its chance. I enjoyed the heck out of it as it really reminded me of some of the things I really missed about the All Hands of yore. It could’ve been a day longer, it could’ve been a little more of a polished experience like Brianna-era events were… but for not having done anything like this for two years, what an accomplishment. I was initially skeptical about spending hours of the first day making small talk deliberately with people you don’t know on the subject of the work you do and how you do it, but once it was framed as an effort to scale-up and operationalize The Hallway Track I suddenly understood it.

I had wishful, hopeful thinking that the genAI bubble would have burst or a new shiny late-stage capitalist exploitation mechanism would be draining focus away from it by now. Alas neither. Though maybe things are beginning? I can dream.

I was also half-wrong about there being a new CEO but not yet sworn in. I didn’t account for the candidate coming from within the org, meaning it could be quick. We’ve had Anthony hired and active for nearly a year now and he’s… fine? So far the changes he’s made to MoCo haven’t been to any of the things I feel we need to change, so I can’t really evaluate him. (Assuming that, since he (well, his reports) are able to evaluate my performance, it’s only fair that I get to evaluate his in turn. That’s how this works, right?)

Predictions for the coming year:

  • We’re going to stick with Anthony as CEO, keeping my count stable
  • Firefox Enterprise is going to be a runaway success, finally giving MoCo that revenue diversification we’ve been aiming for
  • Maybe the genAI bubble will have burst? (Yes, this is a duplicate from last year)
  • I’ll read The Psychology of Software Teams. It will frustrate me to finally be able to name and cite sources for how badly this industry treats its workers.
  • FOG Migration will be “complete” (Legacy Telemetry probes will be gone, code will have been removed) and I’ll move on to working on… maybe data consent management? Developing a way to find out which metrics are unused, and removing them? I have a few ideas.
  • I’ll still be complaining about this Dell XPS laptop, and considering what I’ll be refreshing to the following February. Maybe I’ll finally leave Windows behind?
  • I will have blogged at least three times (setting my prediction deliberately and achievably low), probably mostly video game musings in my ongoing So I’ve Finished series. But I do have a draft of a work post about the history of public data tooling at Mozilla that I’ve been meaning to finish…

To another year of supporting The Mission!

Firefox Tooling Announcements

MozPhab 2.23.0 Released

Bugs resolved in Moz-Phab 2.23.0:

  • bug 2068797 Local test suite runs read and write the real user_info.json
  • bug 2076869 Record a profile of each moz-phab run, and open it in the Firefox Profiler

Discuss these changes in #engineering-workflow on Slack or #Conduit Matrix.

1 post - 1 participant

Read full topic

Firefox Tooling Announcements

Firefox DevTools MCP 0.10.6 released

New release of Firefox DevTools MCP. Available on npm at https://www.npmjs.com/package/@mozilla/firefox-devtools-mcp

Added

  • New print_page tool. It saves the current page as a PDF file and has optional landscape and background options. It uses print CSS, so you can use it to check print styles

Fixed

  • --profile-path now reuses a profile that the server created, instead of creating a new profile inside it. The server marks its profiles with a .firefox-devtools-mcp file. Paths without this marker still use the firefox_devtools_mcp_profile subfolder
  • list_pages, select_page, close_page, navigate_page and new_page no longer bring every tab to the front. They now read the tab list with WebDriver BiDi
  • select_page and close_page now act on the tab from the last tab list, even if tabs were opened or closed since then

Changed

  • list_pages no longer lists tabs that are not loaded yet, for example restored tabs that were never opened
  • If the title of a tab cannot be read within 1 second, the tab is listed with the title (unknown)

Full changelog: Release v0.10.6 · mozilla/firefox-devtools-mcp · GitHub
Repository and issues: GitHub - mozilla/firefox-devtools-mcp: Model Context Protocol server for Firefox DevTools - enables AI assistants to inspect and control Firefox browser through WebDriver BiDi · GitHub
Public chatroom: https://chat.mozilla.org/#/room/#firefox-devtools-mcp:mozilla.org

1 post - 1 participant

Read full topic

Firefox Tooling Announcements

MozPhab 2.22.1 Released

Bugs resolved in Moz-Phab 2.22.1:

  • bug 2078299 Allow overriding base url and repo call sign for development purposes

Discuss these changes in #engineering-workflow on Slack or #Conduit Matrix.

1 post - 1 participant

Read full topic

Firefox Tooling Announcements

MozPhab 2.22.0 Released

Bugs resolved in Moz-Phab 2.22.0:

  • bug 1501077 moz-phab does not update the reviewers list in Phabricator to match local changes
  • bug 2074122 moz-phab --apply-to here --no-branch changed my tree and branch without prompting
  • bug 2077318 moz-phab should block submitting patches that still contain conflict markers
  • bug 2078249 use threading.Event().wait() rather than time.sleep() in moz-phab spinner/wait message
  • bug 2078366 AttributeError fetching revision with Jujutsu
  • bug 2079200 Moz-phab patch doesn’t commit new files when not automatically tracked by jujutsu
  • bug 2082222 Fix some formatting issues after Bug 2074122

Discuss these changes in #engineering-workflow on Slack or #Conduit Matrix.

1 post - 1 participant

Read full topic

The Rust Programming Language Blog

Announcing Rust 1.99.1

The Rust team has published a new point release of Rust, 1.99.1. Rust is a programming language that is empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, getting Rust 1.99.1 is as easy as:

rustup update stable

If you don't have it already, you can get rustup from the appropriate page on our website.

What's in 1.99.1

Rust 1.99.1 fixes the following regressions from prior releases:

  • Missing symbol wcslen: Some Rust targets were getting link errors for missing wcslen, and that function is now present in Rust's compiler-builtins library as a fallback.

    The LLVM loop-idiom-wcslen pass may introduce calls to wcslen as an optimization, and some Rust targets did not have that function readily available in libc or elsewhere. The UEFI targets in particular were broken for any project using std, as some of these calls were introduced in the precompiled standard library, but the issue was also reproducible for similar user code patterns.

  • Miscompilation of FFI bool return values on x86_64: Rust functions returning bool through FFI could leave bits 1-7 of the return register unspecified, causing callers to observe raw values other than 0 or 1. This return type is now marked for zero-extension again, as required by the ABI.

  • rust-lld failed to load libLLVM on macOS: This linker could fail to run if the compiler was invoked without the rustup proxy, as there was no direct setting for the appropriate library search path. Now rust-lld is built with an rpath entry for the directory containing libLLVM.dylib.

If you'd like to help us out by testing future releases, you might consider using the beta (rustup default beta) and nightly (rustup default nightly) channels locally and in your CI. Please report any bugs you might come across!

Contributors to 1.99.1

Many people came together to create Rust 1.99.1. We couldn't have done it without all of you. Thanks!

Niko Matsakis

What agents mean for Rust (and vice versa)

Sheldon breathing into a paper bag.
Me, calmly assessing the implications of LLMs for programming language design(ers).

As someone who has made a career of programming language design, I definitely have moments of existential dread about the rise of LLMs. I started coding in 5th grade on an x861 that ran at 5MHz, had something like a 5MB hard-disk and 512K of RAM2. Computers were really, really different back then. But honestly, programming wasn’t. For all the advances in IDEs and programming languages and so forth, the basic act of entering code into a file has remained surprisingly constant3 – right up until a few years ago, when everything kinda turned on its head. That’s been exciting and empowering at times4 but also scary and disorienting. Will my skills in programming matter? What about my skills in programming language design (such as they are)? This blog post is my attempt to answer those questions.

So as not to keep you in suspense, let me just give you the conclusion. In short, yes, I believe there will still be a role for humans in programming. And yes, I think that programming language design is still relevant. And, naturally, I also happen to think Rust is a really good fit for agentic development – but that’s not to say we can’t make it a better fit. And that’s what this post is ultimately about: what makes Rust and agentic development work well, and where we could double down on those strengths.

This is a kind of “blog post companion version” of a talk I gave this week at REBASE, part of SPLASH. You might also enjoy the slides from the talk I gave at IWACO, IWACO and Rust: hidden links revealed, which was a bit more of a retrospective (no soundtrack for those, though). In any case, this turned out to be a great conference! Jack Huey presented on the Rust Vision Doc and, quite to my surprise, Steve Klabnik and Dorian Scheidt presented “LLMs as Collaborators in Language Specification and Design”, talking about their process in building out Rue. It was fun to see Steve again. On top of that, there was a ton of great research presented at IWACO in particular. I was particularly taken by the work on Reachability Types and Information Borrowing. I also really liked learning about Hylo Lang, particularly their approach to projections. Clever and cool.

Rust is popular in the agentic era

Meme dialogue: “I wrote this in Rust!” “Be honest.” “I am being honest!” “An agent wrote it.” “Thank you.”
From the most important Slack channel at Amazon, #aws-memes.

Rust is popular right now. Very popular:

Stratospheric among the breakout languages is TypeScript, cropped from the chart and sitting currently at +1200% growth from the prior year. [..]

The second most rapid recent growth is in Rust. Meta engineers have written twice as much first-party Rust code so far this year than in the previous 9 years combined.

— David Tolnay (emphasis mine)

Some of that is because agents make it very easy to try out new things. And some of that is because Rust has a reputation for being performant and, if it’s just as easy to pick any language, why not pick one that will yield faster execution times?

But I think Rust is popular for another reason: because Rust happens to be a really good fit for agentic coding6. Of course, I would, wouldn’t I? The point of this post is (1) to argue for why that is and then (2) to discuss what we might change about Rust to make it work even better.

Foundational Software construction with agents

It seems to me that an effective “software factory” will have three pillars:

  • Design: figure out and specify clearly what the code is supposed to do.
  • Construction: author code that does what it is supposed to do.
  • Optimization: optimize the code so that it runs efficiently.

None of these pillars are solved, but some have made more progress than others.

Rust and optimization: fast by default, knobs when you need ’em

Rust is designed to give “good performance by default”, making the idiomatic thing typically also be the fastest thing. But another key part of Rust is that you have the knobs to eke out better performance when you need it. You control when something is stack- or heap-allocated and where pointer indirection occurs. If you want to, you can force the layout of your structs to be done in a particular way, or decide where to introduce boxing into your async stack frames. In the limit, you can put in inline assembly.

In the past, learning how to “tune the performance knobs” could be a bit of a black art: but agents are making that much easier.7 It turns out that agents are really good at fine-tuning the performance of existing code. For example, people who work on super high-performance cryptographic algorithms used to do so by hand-tuning assembly code. But those people tell me that they are now turning to agents for that same job. More prosaicly, a lot of people building network services are finding that, using a profiler like dial9, agents can quite effectively diagnose async performance pitfalls, like long polls or blocking.

Therefore, I think the task for Rust is clear. We keep on with our goal of “low-level enough for a kernel, high level enough for a GUI”. If anything, I think the shift around agents means we can tolerate a bit more “noise” in the form of explicit calls to clone() or share() – but not too much, since we still want the “happy path” of the code to be clear and evident8, even as we surface details that are important in specific scenarios (like tracking precisely how many clones you have of a channel endpoint). Combined with a profiler, you get an effective feedback loop to get your code running fast:

flowchart LR
    Code -- Benchmark --> Profile
    Profile -- Analyzed by --> Agent
    Agent -- Propose --> PR
    PR -- Improve --> Code
  

Reliability is king, as always

As usual though, while a reputation for good performance is what brings people to Rust, what makes them want to stay is a sense that using Rust “just seems to work better”. The basic theory is that Rust’s type system and guardrails help keep agents on track:

I think there’s truth in that: languages like Rust, Ocaml, or Haskell all share this “if it compiles, it works” feeling, and I think that agents will benefit from that.

Testing is not enough, type systems help

Testing can show the presence of bugs, but not their absence.

— Edsger W. Dijkstra, 1970

A woman presents a lopsided cake with the words “Nailed it!”
Agents presenting their work.

Writing tests has never been enough to show that your program is correct – but, with agents, it’s not even close. It happens so often that my first few tests seem great and then the next one goes completely bonkers. Usually bonkers in a way where I cannot even imagine how that bug could have come about. Then I dig in and I realize the agent completely missed the boat on the logic I was trying to express.9

This is where a type systems really shines – particularly an advanced one like Rust. Type systems give you some guardrails that keep the agent on track. In Rust, those guardrails extend beyond simple stuff like “do all fields have the correct names” and to stuff like never having to close a socket.

Type systems are not enough either

Type systems can show the absence of bugs, but not the presence of correctness.

— Me, failing to coin a phrase of equal eloquence10, 2026

So yeah, type systems are helpful! But they have inherent limits. A type system is a simple set of rules that, if you follow them, make sure that you avoid (certain kinds of) mistakes. But they don’t tell you that you do the right thing – only that you don’t do (certain kinds of) wrong things. If you want to go further, you need a proof system – something that will let you show that richer properties are actively true, not just that you’re avoiding bugs.

As it happens, there are a lot of interesting proof tools for Rust, like Verus, Creusot, Aeneas, and Kani. As a starting point, you can prove that your code is panic-free, which will eliminate a lot of bugs, though it is still fundamentally a “type-checker-like” property (i.e., you avoid doing things wrong). But you can also go further and add assertions that show your code is computing the results you expect.

Strong guardrails give better performance

Deplying proofs is not just good for getting code right – it’s good for getting code fast. This is somewhat counterintuitive, but it’s true. Earlier I mentioned how optimized cryptographic algorithms were now being written by agents. Part of what makes that possible is that cryptographic algorithms have a very clear mathematical definition. This means that you can basically tell the agent “do whatever you want, as long as you can prove that the code still computes the correct result”. These proofs can be done with theorem provers like Lean or Rocq. The blog post “Why Lean is faster than Rust” makes the point quite well.

For Rust, a good example of this would be around unsafe code. Right now, people are sometimes reluctant to deploy unsafe code even if it might make their code faster because it’s not worth the risk of getting it wrong. And I applaud that effort. But what if you could use a prover to show that your unsafe code is, in fact, safe (i.e., incapable of causing undefined behavior)? Then you’d be free to optimize to your heart’s content.

If we are going to prove things, do we need type systems?

You might wonder: if we’re deploying a proof system on our code, do we still need type systems? I think the answer is definitely yes. Type systems are infinitely simpler to work with, which is why they are much more popular. Using a type system lets you rule out all kinds of annoying bugs so that you can focus your “proof energy” only on the important stuff.

As a simple example of this, consider the following Java code:

voidsetFoo(Barbar){this.foo=bar;assertthis.foo==bar;}

Without seeing more code, it is impossible to prove whether this assertion is true or not! This is because there could be any number of other threads running around mutating this. In contrast, the following Rust code must be true (at least, so long as the Eq operator is reflexive for Bar):

fn set_foo(&mutself,bar: Bar){self.foo=bar;assert_eq!(self.foo,bar);}

We know this because the type system of Rust guarantees that &mut self implies a unique reference.

This is in fact exactly how Verus works. By trusting the Rust borrow checker to track uniqueness etc, it simplifies the job of the SMT solver, making proof checking must faster and more reliable than it would be otherwise.

Where I think Rust should go on reliability

So my take is that Rust should double down on if it compiles, it works. As expressive as Rust’s type system is, there are plenty of things that it cannot capture. This includes fairly common things, like this method only reads one particular field or internal references, but also fancy things like “no logging of personally identifiable information”. We should work to lift those limits, giving agents room to prove more complex properties.

We also need to improve the experience of integrating a proof tool by working towards an extensible compiler. As cool as the existing Rust-based proof tools are, using them is a janky experience involing annoying macros and pinned compiler versions. We can do better.

A north star: No more unsafe code

I’d like to propose a north star for Rust: no more unsafe code. By that, I don’t mean that we never use the unsafe keyword. Rather, I mean that we have the power to prove that all the unsafe code we write is correct and cannot cause undefined behavior. Don’t get me wrong: knowing exactly what code needs to be vetted is a huge step forward, as is normalizing the idea that libraries need to be responsible for protecting their users from undefined behavior via safe API boundaries11. But given agents’ ability to find vulnerabilities, I think we need to up-our-game if we want to stop the steady stream of CVEs.12

Design is an open problem

But before you go about proving things or optimizing them, you need to have a clear idea of what you are building. As far as I can tell the state of the art here is basically variations on “markdown-driven-development”, developing docs and hoping that agents pay attention.

I don’t know how much of a role for Rust there is in this space. I think there are some steps we might wind up taking that could be helpful here – for example, having ways to separate out interface from implementation so that it’s easier to review the interface in isolation or to see when the agent made changes there.13 Or perhaps just providing betters tools to review that.

I also think there’s space for dedicated “modeling languages”, something that let you iterate quickly with agents on the high-level designs and invariants without having to get into the weeds and read the code. I have more thoughts on this but it merits many blog posts all of its own. For the moment, I’ll start by pointing you all at this post by Kari Wilhelm, “Principles of Anchor Engineering”, which I think does a nice job of describing what it means to pull out the key elements of a design and drive the coding from there.

Conclusion

We covered a lot of ground here, so let me put this all together. What makes Rust a good choice for developing with agents?

  • It offers good default perf (why pay more?) and, in conjunction with a profiler, can be tuned as much as you want.
  • The type system gives stronger guardrails, and if you setup your agents right, you can strengthen those guardrails through techniques like new types.

And what could make it better, from a language design perspective? Overall, I think it’s very similar to what we were doing, except that the way we weight the tradeoffs is tweaked to account for agents doing the code authoring, and humans focused more on code comprehension:

  • If it compiles, it works – our type system is our big advantage, we need to make it more expressive and more functional so that agents don’t get stuck trying to work around it!
    • And as an adjunct, establish a North Star: Enable Unsafe code without unsafety by supporting proof, and see the most widely used Rust libraries proven sound.
  • Low-level enough for a kernel, high-level enough for a GUI – make sure we expose the knobs needed to tune for performance, while also making the code’s intent clear (but don’t worry about golfing the code down to an absolute minimum number of characters).
  • Extensible and versatile – one of Rust’s great strengths is that, with macros + our type system, we support DSLs and a wide variety of targets. We need to bring that spirit of extensibility into the compiler so we can target more styles of development (e.g., GPU, deep language interop, etc) and also integrate theorem provers and other tools that require more than just tokens.

As a final close, let me point you at this interesting article by Marc Brooker, “What’s easy now? What’s hard now?”. He posits that the more it is possible to clearly specify success criteria for a task, the easier it is, and that we may find that tasks we traditionally think of as ‘hard’ (e.g., building out an efficient implementation of a cryptographic algorithm) may soon become automated, whereas tasks that we have thoght of as ’easy’ (e.g., making a unique, compelling website) may become more valued.

Looking at it from a PL point-of-view, I think Marc’s point reinforces my sense that the biggest action is going to come from design languages, things that let you capture the architecture and intended behavior at a very high-level. The goal would be to let humans intuitively specify a design that gets translated into effective guardrails, whether that’s type system checks, lints, or theorems to be proven. Agents would then take on the task of translating the design into code written in a traditional PL like Rust, with humans looking at it only rarely. This doesn’t make the design of traditional PLs less interesting, but it definitely makes the tradeoffs very different than they were before.


  1. Secretly I have always been jealous of people that coded using punchcards and stored things on gigantic rolls of tape or whatever. I just want to be world weary so badly! I am kinda delighted that I’m finally old enough that I can say things like “I remember when we had to edit CONFIG.SYS before we could play a computer game!” and younger programmers will gasp in horror.5 But I still feel like I must sound like a silly kid to folks who were around “back in the day”. ↩︎

  2. I still remember when my dad told me he had bought me a 10MB disk and secretly he had bought a 40MB disk to surprise me. It seemed absurdly large. “I’ll never need a new disk again!” ↩︎

  3. I mean my god, we’re even using vi again! My first editor! ↩︎

  4. I built an electron app! And it was so easy to do! And reading weird papers and things is so much more accessible now that I can ask an LLM to translate all the math jargon for me. ↩︎

  5. I remember once trying to tell Alex Crichton how I used to “order shareware from a paper catalog by putting a check in the mail” and he was like “I do not understand any of the nouns in that sentence”. Highlight of my life, right after Rust 1.0. Oh, and maybe the birth of my child and getting married. I guess. ↩︎

  6. I’m not saying Rust is the only language that’s a good fit. I’m not even saying that the way that Rust is a good fit is the only way to be a good fit. I have heard for example that Go is a really good fit for agentic coding and I believe it: I think it hits a different sweet spot, one that helps agents iterate quickly (although I do suspect agents will let data races and subtle bugs slip through that Rust would have prevented). ↩︎

  7. The fact that agentic development makes more room for knobs is good for Rust performance, but it is quite likely an even better fit for performance in other languages. Tuning a GC has traditionally been a job that really requires a GC expert, but agents can likely fill that role pretty well these days. ↩︎

  8. On this pont, I’m even more convinced of the importance of ergonomic RC now that I work with agents. Agents are good at introducing the extra clones and things you need to deal with capturing around async tasks, but the resulting code is so annoying to read, it really hides what’s going on. ↩︎

  9. “Claude, this is not what we talked about!” – me, all too often. ↩︎

  10. So close though, right? I just .. couldn’t land the plane. ↩︎

  11. All too often, C++ libraries consider it adequate to tell users “you’re holding it wrong” – i.e., that they are responsible for causing undefined behavior by misusing the API. Rust takes a clear position that a safe API has to protect its users, no matter how much they abuse it. ↩︎

  12. To be fair, if Rust is encountering a stream of CVEs, other languages like C++ and Java are encountering a veritable flood, since they lack the protection of Rust’s type system. st takes a clear position that a safe API has to protect its users, no matter how much they abuse it. ↩︎

  13. The revenge of .h files? ↩︎

This Week In Rust

This Week in Rust 673

Hello and welcome to another issue of This Week in Rust! Rust is a programming language empowering everyone to build reliable and efficient software. This is a weekly summary of its progress and community. Want something mentioned? Tag us at @thisweekinrust.bsky.social on Bluesky or @ThisWeekinRust on mastodon.social, or send us a pull request. Want to get involved? We love contributions.

This Week in Rust is openly developed on GitHub and archives can be viewed at this-week-in-rust.org. If you find any errors in this week's issue, please submit a PR.

Want TWIR in your inbox? Subscribe here.

Updates from Rust Community

Official
Foundation
Newsletters
Project/Tooling Updates
Observations/Thoughts
Rust Walkthroughs

Crate of the Week

This week's crate is hotcode, a crate for hot-reloading Rust functions at runtime.

Thanks to alordash for the self-suggestion!

Please submit your suggestions and votes for next week!

Calls for Testing

An important step for RFC implementation is for people to experiment with the implementation and give feedback, especially before stabilization.

If you are a feature implementer and would like your RFC to appear in this list, add a call-for-testing label to your RFC along with a comment providing testing instructions and/or guidance on which aspect(s) of the feature need testing.

No calls for testing were issued this week by Rust, Cargo, Rustup or Rust language RFCs.

Let us know if you would like your feature to be tracked as a part of this list.

Call for Participation; projects and speakers

CFP - Projects

Always wanted to contribute to open-source projects but did not know where to start? Every week we highlight some tasks from the Rust community for you to pick and get started!

Some of these tasks may also have mentors available, visit the task page for more information.

No Calls for participation were submitted this week.

If you are a Rust project owner and are looking for contributors, please submit tasks here or through a PR to TWiR or by reaching out on Bluesky or Mastodon!

CFP - Events

Are you a new or experienced speaker looking for a place to share something cool? This section highlights events that are being planned and are accepting submissions to join their event as a speaker.

No Calls for papers or presentations were submitted this week.

If you are an event organizer hoping to expand the reach of your event, please submit a link to the website through a PR to TWiR or by reaching out on Bluesky or Mastodon!

Updates from the Rust Project

603 pull requests were merged in the last week

Compiler
Library
Cargo
Rustdoc
Clippy
Rust-Analyzer
Rust Compiler Performance Triage

No significant improvements or regressions this week. Most of the PRs marked as improvements had a similar perf. result, which was caused by codegen noise in the measurements.

There were some nice wins for memory usage though, thanks to #163915 and especially #163887.

Triage done by @Kobzol. Revision range: cc9a14f7..7211ea59

Summary:

(instructions:u) mean range count
Regressions ❌
(primary)
0.2% [0.1%, 0.6%] 73
Regressions ❌
(secondary)
0.3% [0.1%, 1.4%] 57
Improvements ✅
(primary)
-0.3% [-1.0%, -0.1%] 21
Improvements ✅
(secondary)
-0.3% [-1.7%, -0.1%] 28
All ❌✅ (primary) 0.1% [-1.0%, 0.6%] 94

3 Regressions, 21 Improvements, 2 Mixed; 1 of them in rollups 27 artifact comparisons made in total

Full report here.

Approved RFCs

Changes to Rust follow the Rust RFC (request for comments) process. These are the RFCs that were approved for implementation this week:

  • No RFCs were approved this week.
Final Comment Period

Every week, the team announces the 'final comment period' for RFCs and key PRs which are reaching a decision. Express your opinions now.

Tracking Issues & PRs
Rust Rust RFCs Language Reference Leadership Council

No Items entered Final Comment Period this week for Cargo, Compiler Team, Language Team or Unsafe Code Guidelines. Let us know if you would like your PRs, Tracking Issues or RFCs to be tracked as a part of this list.

New and Updated RFCs

Upcoming Events

Rusty Events between 2026-10-14 - 2026-11-11 🦀

Virtual
Asia
Europe
North America
South America

If you are running a Rust event please add it to the calendar to get it mentioned here. Please remember to add a link to the event too. Email the Rust Community Team for access.

Jobs

Please see the latest Who's Hiring thread on r/rust

Quote of the Week

I think that there are a nonzero number of people who are experts in the field who would genuinely respond to "how does C work?" with "it doesn't, next question"

– Clar Fon at the rust-lang zulip

Thanks to kleines Filmröllchen for the suggestion!

Please submit quotes and vote for next week!

This Week in Rust is edited by:

Email list hosting is sponsored by The Rust Foundation

Discuss on r/rust

Firefox Tooling Announcements

Happy BMO Push Day! (20261013.2)

Github Link

The following changes have been pushed to bugzilla.mozilla.org:

  • Bug 2079152 - Unable to select content-type when creating attachment
  • Bug 2079453 - Upgrade devbridge-autocomplete to 2.0.1 and fix autocomplete dropdown reopening after the field loses focus
  • Bug 2065173 - Migrate Group REST resource to native Mojo API
  • Bug 2078690 - Migrate User REST resource to native Mojo API: user objects (get, create, update, suggest, whoami, offer_account_by_email)
  • Bug 2079991 - When one update requests needinfo from several users , the webhook’s event.changes has a single flag.needinfo entry for the last requestee only; the others are dropped.

Discuss these changes in the BMO Matrix Room

1 post - 1 participant

Read full topic

Firefox Tooling Announcements

Firefox Profiler Deployment (October 13, 2026)

The latest version of the Firefox Profiler is now live! Check out the full changelog below to see what’s changed:

Highlights:

  • [Markus Stange] Speed up activity graph drawing (#5888)
  • [Florian Quèze] profiler-cli: accept several marker handles and ranges in marker info (#6277)
  • [Nazım Can Altınova] Add “profiler-cli profile save ” (#6340)
  • [Nazım Can Altınova] Add global `–permalink` and `–short-permalink` flags to profiler-cli (#6338)
  • [Markus Stange] Serve production build by default during local development (#6353)
  • [Nazım Can Altınova] Add global `–permalink` and `–short-permalink` flags to profiler-cli (#6338)
  • [Nazım Can Altınova] Add the ability to apply source maps to the profiles using `profiler-edit` (#6382)

Other Changes:

  • [Nazım Can Altınova] Fix the upload progress bar not showing in light mode (#6348)
  • [Nazım Can Altınova] Fix the variable name passed to the malformed response error strings (#6351)
  • [Markus Stange] Add missing await when calling Chrome importer (#6346)
  • [Markus Stange] Use “falls through” for all fallthrough comments (#6344)
  • [Markus Stange] Move source map related profile logic files into profile-logic/source-maps/ (#6349)
  • [Markus Stange] Make the source view actually read-only. (#6313)
  • [Markus Stange] Run CI on merge queue branches (#6352)
  • [Nazım Can Altınova] Fix “… was not wrapped in act” warnings in tests (#6362)
  • [Nazım Can Altınova] Add a scope tree test for shorthand destructuring defaults (#6367)
  • [fatadel] Accept --session before subcommands in profiler-cli (#6358)
  • [Nazım Can Altınova] Replace the deprecated loose option with Babel assumptions (#6366)
  • [fatadel] Clarify how to save profiles for later access (#6355)
  • [Nazım Can Altınova] Make `yarn test-cli` and `yarn test-all` faster (#6369)
  • [Nazım Can Altınova] Use a profile with tab information in the tab selector URL tests (#6370)
  • [fatadel] Move FileIO table labels into the Gecko profile upgrader (#6375)
  • [fatadel] Clear preview selections when zooming out in the CLI (#6380)
  • [fatadel] :clockwise_vertical_arrows: Sync: l10n → main (October 13, 2026) (#6389)

Big thanks to our amazing localizers for making this release possible:

  • de: Ralf Duehnfahr
  • el: Jim Spentzos
  • en-GB: Ian Neal
  • es-CL: ravmn
  • fr: Théo Chevalier
  • fy-NL: Fjoerfoks
  • ia: Melo46
  • it: Francesco Lodolo
  • nl: Fjoerfoks
  • ro: IulianArian
  • ro: robbp
  • ru: Valery Ledovskoy
  • sv-SE: Andreas Pettersson
  • tr: Selim Şumlu
  • tr: Sena nur
  • zh-TW: Pin-guang Chen

Find out more about the Firefox Profiler on profiler.firefox.com! If you have any questions, join the discussion on our Matrix channel!

1 post - 1 participant

Read full topic

Niko Matsakis

Why 'externalized' proofs of cyclic trait impls does not work

For this post, I wanted to talk about two different approaches to handling supertraits. I’m calling them modular proofs vs external proofs. The key idea of this post is that, if we want to have cyclic trait impls, we really need to use a modular proof strategy, where the impl establishes all supertraits hold. Previously we had considered an external strategy, where the piece of code using the impl has the obligation to prove the supertraits hold. Modular proofs always seemed better but I did not think they were workable in the past. But I have become convinced that external proofs are incompatible with Rust as designed, and hence modular proofs are really the only option1. This post dives into that reasoning, and also gives a bit of explanation of what I mean by proofs in the first place.

Traits and supertraits

So what do I mean by modular vs external proofs? Well, it all comes down to who is responsible for proving that supertrait obligations hold. Consider a trait like Magic:

traitMagic: Copy {}

The supertrait declaration means that, whenever X: Magic for some type X, it should be true that X: Copy. We make use of this in generic functions:

fn is_copy<T: Copy>(){}fn is_magic<T: Magic>(){// Legal, because `T: Magic` implies `T: Copy`
is_copy::<T>();}

The trick is that the compiler has to make sure that this implication holds – i.e., for every type X that implements Magic, X also implements Copy. So how does it do it?

Modular proofs: the impl must show supertraits hold

The obvious answer is to make proving supertraits part of deciding whether an impl is valid. For any impl of Magic, we can require that the Copy supertrait holds. So an impl like this would be illegal:

// In a modular system, this impl is *illegal*
implMagicforString{}

This impl is illegal because it would require that String: Copy, and that does not hold. Seems good.

Modular proofs are a bit tricky

I am calling these proofs modular because the idea is that we can prove an entire program is valid by proving each part of it separately. In “programming language” theory, this is typically called a “modular” check, as it works by breaking up the entire program into modules that can be independently checked.

The idea with a modular proof is that we can trust impls to show that the supertrait relationships hold, we don’t have to go and re-prove them over and over. If the impl is wrong, the impl will be invalid, but our code is fine. So if we have impl Magic for String, that implies the rest of the program can prove that String: Magic:

fn string_is_magic(){// Legal, because there is an impl for `String: Magic`:
is_magic::<String>();}

In fact, since we know that Magic implies Copy, the rest of the program can even rely on impl Magic for String to conclude that String: Copy:

fn string_is_copy(){// Legal, because there is an impl for `String: Magic`,
// and `Magic` implies `Copy`:
is_copy::<String>();}

So long as impl Magic for String is invalid, none of this poses a problem to soundness, since the program overall doesn’t type-check.

Comparison with functions

An easy way to understand the idea of modular checks is to think of functions. Imagine you have a function like this one:

fn compute_sum(a: i32,b: i32)-> i32 {format!("{a} + {b}")// <-- Error
}

Clearly, this function is not legal. It takes two integers and promises to return a third integer, but in fact it returns a String. So the function is illegal. But if you have a call to that function from elsewhere, we consider that other call to be legal:

fn use_sum(){letc: i32 =compute_sum(2,20);// OK
}

Here, use_sum is relying on compute_sum to obey its contract. It’s not the job of use_sum to check that, it can just assume it is true.

The catch: how do we decide the impl is invalid

There is a bit of a catch though. How do we decide if the impl is invalid? The basic idea was that impl Magic for String would have to prove that String: Copy. But we just saw that it could, in fact, do that by using itself. In other words, if we aren’t careful, we can provide a proof that String: Copy like…

  • String: Copy because Magic implies Copy and
    • String: Magic because impl Magic for String exists

and then we would (incorrectly) conclude that the impl is valid. So clearly we need to do something to rule that out. We need a rule that says, when we are proving that an impl is valid, that proof cannot recursively rely on the impl itself.[^termination] I’ll come back in a future post to ways we might do that, but for now, I want to explore another alternative.

External proofs: the user of the impl must show supertraits hold

When we first looked at this problem, way back in 2018 or so, we thought of another approach. What if we said that an impl is not responsible for proving supertraits. Instead, the idea would be that impl Magic for String is not enough to say that String: Magic. It only says that Shallow(String: Magic) – i.e., String implements Magic in a shallow way, but not in a deep way that includes the full supertraits. To prove that String: Magic, we have to show that Shallow(String: Magic) and Shallow(String: Copy):2

Shallow(String: Magic)
Shallow(String: Copy)
---------------------------- Magic fully implemented
String: Magic

This has the somewhat counterintuitive implication that impl Magic for String is actually legal in an “external proof” approach:

// In an external system, this impl is LEGAL
// (but unusable)
implMagicforString{}

The saving grace is that, while this impl is legal, you can’t actually use it. This function for example does not compile:

fn string_is_magic(){// NOT legal in an external system:
// * We can prove that `Shallow(String: Magic)`
// * We CANNOT prove that `Shallow(String: Copy)`.
is_magic::<String>();}

Here, String: Magic doesn’t hold even though there is an impl of Magic for String, because the caller also has to check that String: Copy is implemented, and it is not. Huh, interesting.

Comparison to functions: external is awkward

the “external proof” approach for impls is clearly a bit awkward. If we make the comparison to functions, it’s as if the caller has to double check that the callee’s body matches its return type, it can’t actually trust the declared signature. But, awkward or not, it does resolve our problem: given impl Magic for String, we cannot prove String: Copy, and hence we cannot prove that String: Magic. We can only prove that Shallow(Magic: String), which doesn’t imply that the supertraits hold.

But external doesn’t work with unsafe traits

Based on the above, for a long time, I was working with the assumption that, weird as they are, we would go with the “external proof” approach. However, as Ralf Jung and lcnr pointed out to me recently, this is very challenging to reconcile with unsafe traits. Consider an unsafe trait like Nullable:

// A type that can be safely transmuted from `0_usize`.
unsafetraitNullWord{}

The way that Rust works, when we write an unsafe impl, it is the job of that impl to prove that the unsafe conditions hold. Other parts of the program get to trust the impl. So if I write a function like this one, it should be considered safe:3

fn foo<T: NullWord>()-> T{std::mem::transmute(0_usize)}

Now imagine that I wrote an invalid impl like this one:

// INVALID: We are asserting that `Box` can be null,
// which is not true!
unsafeimpl<T>NullableforBox<T>{}

Given this program I could clearly call foo::<Box<u32>>(), but that would “go wrong” (cause “undefined behavior”). I think we would all agree that the fault lies in the impl. And yet, that is inconsistent: we say that the impl alone cannot be trusted to figure out if the supertraits are implemented, but it can be trusted to figure out if the unsafe impl is valid?

Conclusion

I definitely believe that we want to treat the “extra conditions indicated by unsafe” as a more general version of the other obligations that an impl has to establish to show that the trait holds– and therefore that we must have modular proofs. That’s kind of a relief, because something always felt wrong about external proofs, but it was hard to put my finger on a concrete problem. In the next post in this series (whenever that may be…), I expect to cover the approach to coinductive modular proofs that I landed on. Then I expect to talk about an alternative that was proposed to me that I find quite appealing.


  1. I think this was obvious to Ralf Jung from the start. But it took me a bit. ↩︎

  2. This notation is called an inference rule. The conditions above the line are the premises and the bottom line is the conclusion. It says that, if you know the premises are true, you can infer that the conclusion holds. ↩︎

  3. In point of fact, I believe this will not compile because of special rules about unsafe, but that’s not relevant to the point I’m trying to make. ↩︎

Firefox Tooling Announcements

Firefox DevTools MCP 0.10.5 released

New release of Firefox DevTools MCP. Available on npm at https://www.npmjs.com/package/@mozilla/firefox-devtools-mcp

Fixed

  • set_firefox_prefs and get_firefox_prefs work again. They now use WebDriver BiDi to run in the privileged context
  • list_pages no longer fails when the URL or title of a tab cannot be read. That tab is listed with (unknown) values instead

Changed

  • take_snapshot and the UID-based input tools now use WebDriver BiDi instead of WebDriver Classic
  • The snapshot script now runs in a sandbox, so page scripts cannot interfere with it and it does not add globals to the page
  • The Cursor plugin manifest is replaced by the root plugin.json and mcp.json files from the Agent Plugins 1.0 standard, which cursor.directory reads

Full changelog: Release v0.10.5 · mozilla/firefox-devtools-mcp · GitHub
Repository and issues: GitHub - mozilla/firefox-devtools-mcp: Model Context Protocol server for Firefox DevTools - enables AI assistants to inspect and control Firefox browser through WebDriver BiDi · GitHub
Public chatroom: https://chat.mozilla.org/#/room/#firefox-devtools-mcp:mozilla.org

1 post - 1 participant

Read full topic

The Servo Blog

August in Servo: CSS Grid, text selection, and more!

Servo 0.6.0 contains all of the changes we landed in August, which came out to 417 commits!

The big news is that we’ve turned on support for CSS Grid by default (@nicoburns, #45621). This has had a huge positive impact on the visual appearance of many websites!

We’ve shipped several new web platform features:

Plus a bunch of new DOM APIs:

This release also contains significant improvements to navigation, document loading, and session history, which are some of the gnarliest parts of the HTML specification. Who knew that loading about:blank was so complicated? The upshot to all this work is that pages that open additional windows or include iframes work much more reliably.

This is another big update, so here’s an outline:

Security

We upgraded to SpiderMonkey v153, which brings many new security and crash fixes to Servo’s JavaScript engine integration (@sagudev, @Gae24, #47489).

Navigating away from a document that activated fullscreen mode now exits fullscreen mode (@shubhamg13, @jdm, #47123).

Real world compat

CSS Grid enabled by default means that many websites with traditional grid layouts now look correct, like rustweek.org.

v0.5.0
v0.6.0
2026.rustweek.org

It also makes a lot of websites look better that you wouldn’t assume rely on CSS Grid:

v0.5.0
v0.6.0
labs.jensimmons.com

Panning gestures initiated by touch inputs now lock to the dominant axis (@yezhizhen, #46983). This results in more predictable panning when interacting with pages in Servo.

We fixed a crash affecting gumroad.com.au (@lumiscosity, #47266).

We’re interested to hear how well your favourite websites run in Servo! Report successes in this Zulip thread or this Github discussion, and failures in our GitHub issues.

Work in progress

Accessibility support in Servo continues to make great strides; it can be enabled with the --pref accessibility_enabled command line argument. Servo now reports positions and sizes for items in the content accessibility tree (@philocalyst, @alice, @mrobinson, #46996), allowing macOS VoiceOver to work more reliably. We also report many ARIA roles for web content elements to accessibility toolkits (@mukilan, #47318).

Full list of supported roles
  • alert
  • banner
  • blockquote
  • caption
  • code
  • complementary
  • contentinfo
  • definition
  • deletion
  • directory
  • document
  • emphasis
  • feed
  • figure
  • generic
  • insertion
  • list
  • log
  • main
  • math
  • navigation
  • none
  • note
  • paragraph
  • presentation
  • rowgroup
  • search
  • status
  • strong
  • term
  • time
  • timer

Servo’s support for Document.execCommand() is rapidly improving, and can be enabled with --pref dom_exec_command_enabled. We fixed issues with the inserttext command (@Psychpsyo, #47224, #47306), editing uneditable content (@Psychpsyo, #47362), computing contained nodes (@TimvdLippe, #47429), adjusting selections in contenteditable (@mrobinson, #47393, #47581, #47581), and implemented the indent and insertLineBreak commands (@Psychpsyo, #47416, #47445).

We made a lot of progress on the LargestContentfulPaint performance metric, implemented behind the --pref largest_contentful_paint_enabled argument. Servo now supports the element and loadTime attributes, (@shubhamg13, #46553, #47475), URLs for video element posters (@shubhamg13, #43376), accurate size information (@shubhamg13, #43378), and painting text nodes (@shubhamg13, #47212).

There was preparatory work for a future AudioWorklet implementation (@niyabits, #46856, #47016, #47199). See Niya’s blog post about her Outreachy project for more details!

We’re continuing work on the ServiceWorker feature, which can be enabled with --pref dom_serviceworker_enabled. This month we implemented the CookieStore interface behind the --pref dom_cookiestore_enabled command line argument (@Taym95, #47044) as well as the Cache interface (@gterzian, #47220).

Servo now supports a disk-backed HTTP cache that can be enabled with --pref network_http_disk_cache (@Narfinger, #43819). This cache persists between runs, but there is not yet any way to clear it.

Embedding API

Breaking change: WebView::toggle_sampling_profiler has been removed (@lumiscosity, #47014). To perform sampling profiling on Servo, follow the instructions in the book.

Breaking change: The variants of MouseButton have been renamed to Primary, Secondary, and Auxiliary (@mrobinson, @SimonSapin, #47327).

We’ve added several new Cargo features reflecting common usage patterns:

  • default_web_features, to include optional web platform features in the build (e.g. WebXR and WebGPU) (@jschwe, #44032)
  • bundled, to include required resources in the binary (@jschwe, #44032)
  • brotli-compression-stream, enabling support for brotli content in the CompressionStream/DecompressionStream web APIs (@Narfinger, @jdm, #47197)
  • webcrypto, to enable support for the Crypto and WebCrypto web APIs (@Narfinger, #47198)
  • webgl, to enable support for WebGL (@janeoa, #47200)

For users and developers

One visible change is that interactive text selection now works! This is the result of lots of work across our script and layout code to ensure it respects the actual geometry of the page content (@mrobinson, @SimonSapin, #47022, #47025, #47329, #47358, #46982, #47425, #47438, #47437, #47581, #47070, #47048, #46963).

Building Servo now requires at least Clang 19 on all platforms (@jschwe, #47421). Additionally, the cargo-ohos tool is now required for building for OpenHarmony (@jschwe, #47334).

Debug builds on Windows can now complete successfully (@jschwe, #47372).

Linux arm64 builds have been customized to work on Raspberry Pi 5 (@mukilan, #47113). Any libservo embedders targeting the same platform will require a similar change if they are using jemalloc.

servoshell

Under Wayland and OpenHarmony, we now integrate with the system clipboard (@TheZoq2, @yezhizhen, @jschwe, #46938, #47177).

The macOS command key and Windows logo key is now reported as Meta instead of Super (@eachra-bawn, #47330).

The Android port has had many rough edges filed off as part of modernizing it for the Compose UI (@veyndan, #47656, #47491, #47494, #47697, #46955, #47644, #47652, #47276, #47277, #47299, #47439) and converting its implementation to Kotlin (@veyndan, #46926, #46925, #46951, #46965). Library consumers are no longer responsible for implementing pause and resume support (@veyndan, #47555).

Meanwhile, the OpenHarmony port now supports external keyboards with US keyboard layouts (@jschwe, #47359). Additionally, there are now better fallback font selections for CJK characters (@RichardTjokroutomo, #47116, #47664).

Setting the device_pixel_ratio_override preference is no longer ignored on Android and OpenHarmony (@jschwe, #47081).

Performance and stability

There were many small changes this month that removed unnecessary allocations (@Narfinger, @jschwe, @yezhizhen, #47264, #47147, #47066, #47249, #47249, #47285, #47307, #47343, #47361, #47395, #47427, #47454, #47474, #47506, #47479, #47530, #47541, #47566, #47509, #47622).

We also reduced GC interactions leading to better JS performance (@jdm, @Narfinger, @webbeef, @TimvdLippe, @mrobinson, #47513, #47250, #47158, #47165, #47049, #47251, #47510, #46981).

On platforms that memory map font data (such as OpenHarmony and macOS), we can now save time that would otherwise be spent loading font files from disk (@jschwe, #47029). Similarly, reading system fonts is no longer in the critical path of starting up the engine (@Narfinger, @jdm, #46476).

There were were also a number of savings in the final binary size (@jschwe, @kkoyung, @Narfinger, @jschwe, #47211, #47214, #47210, #46950).

We fixed crashes related to media playback (@calvaris, #46891), the 2d canvas renderer (@yezhizhen, #46915), fonts with negative line gaps (@jschwe, #47374), and resolving fonts variants (@simonwuelker, #47356).

Canvas renderer worker threads can now be adjusted by the thread_pool_canvas_workers preference, allowing more performance granularity according to embedder needs. (@jschwe, #46691, #47018)

We’ve continued our long-running effort to use the Rust type system to make Servo’s integration with SpiderMonkey safer and more reliable (@jdm, @treetmitterglad, @Narfinger, @Gae24, #47398, #47296, #47051, #47005, #47323, #47297, #47322, #47368, #47446, #47317). We’re also working on statically preventing a footgun that can lead to uncollectible GC cycles and leaking memory (@Gae24, #47355, #47650).

Historically, about:memory has reported all memory usage by Rust implementations of JS APIs as one undifferentiated js/malloc-heap number. We now break it down by memory usage of nodes in document trees (@pylbrecht, #46931) and interfaces that are not part of the DOM (@jdm, #47531). We also report memory usage for the network stack’s cookie database (@Narfinger, #47248) and the canvas renderer backend (@janeoa, @jdm, #46958).

We’ve added an experimental threadboost mechanism (@jschwe, @mrobinson, #46843, #47027) that has been observed to improve Speedometer results by 50% and allow canvas rendering to reach 60 FPS. This is initially enabled for OpenHarmony, but future expansion to other Linux-based platforms is expected.

More on the web platform

We upgraded to resvg 0.48, fixing many issues with SVG images with missing width/height attributes that specified a viewBox (@nicoburns, #46937).

Upgrading to Taffy 0.14 fixed a number of CSS grid-related crashes, support for grid names, better intrinsic sizing, and improved performance (@nicoburns, #47031, #47493). We also fixed some issues in the Taffy grid integration for more accurate baselines (@nicoburns, #47518).

Session history traversals (i.e. navigating backwards or forwards in history) now wait for any outstanding traversal to complete (@mrobinson, #47293, #47485). Meanwhile, navigations to about:blank are skipped if the initial about:blank document is present (@jdm, #47074).

about:blank documents no longer use the asynchronous HTML parser (@jdm, #47050), and no longer report a readyState of LOADING (@mrobinson, #47450). Opening auxiliary windows will wait to dispatch the load event until the initial about:blank document has been replaced (@jdm, @mrobinson, #47442, #46975).

For non-about:blank content, we also made the document loading phases more reliable (@TimvdLippe, @mrobinson, #47426), improved conformance of javascript: URL evaluation and navigation target selection (@mrobinson, #47486, #47516), and addressed longstanding compatibility issues with cross-origin Window and Location objects (@mrobinson, #47549).

Our media playback has improved; we fixed several issues around canPlayType() on HTMLMediaElement that allow Servo to report that MP4 content is playable (@jdm, #47612, #47641, #47670).

Input elements with the autofocus property inside of a <dialog> now receive focus when that dialog is opened (@skyz1, @mrobinson, #45826).

We improved the exception error text for numerous web APIs (@jdm, @eachra-bawn, #46960, #47124, #47168, #47580, #47430, #47127).

We also fixed issues related to many other APIs, including:

New contributors

A special thanks to the following people for landing their first patch in Servo:

Interested in helping build a web browser? Take a look at our curated list of issues that are good for new contributors!

Donations

Thanks again for your generous support! We are now receiving 8128 USD/month (+3% from July) in recurring donations. This helps us cover the cost of our speedy CI and benchmarking servers, one of our latest Outreachy interns, and funding maintainer work that helps more people contribute to Servo.

Servo is also on thanks.dev, and already 36 GitHub users (+1 from July) that depend on Servo are sponsoring us there. If you use Servo libraries like url, html5ever, selectors, or cssparser, signing up for thanks.dev could be a good way for you (or your employer) to give back to the community.

We now have sponsorship tiers that allow you or your organisation to donate to the Servo project with public acknowlegement of your support. If you’re interested in this kind of sponsorship, please contact us at join@servo.org.

8128 USD/month
10000

Use of donations is decided transparently via the Technical Steering Committee’s public funding request process, and active proposals are tracked in servo/project#187. For more details, head to our Sponsorship page.

This Week In Rust

This Week in Rust 672

Hello and welcome to another issue of This Week in Rust! Rust is a programming language empowering everyone to build reliable and efficient software. This is a weekly summary of its progress and community. Want something mentioned? Tag us at @thisweekinrust.bsky.social on Bluesky or @ThisWeekinRust on mastodon.social, or send us a pull request. Want to get involved? We love contributions.

This Week in Rust is openly developed on GitHub and archives can be viewed at this-week-in-rust.org. If you find any errors in this week's issue, please submit a PR.

Want TWIR in your inbox? Subscribe here.

Updates from Rust Community

Official
Foundation
Newsletters
Project/Tooling Updates
Observations/Thoughts
Rust Walkthroughs

Crate of the Week

This week's crate is karatepe, a statically typed localisation language and library.

Thanks to miro for the self-suggestion!

Please submit your suggestions and votes for next week!

Calls for Testing

An important step for RFC implementation is for people to experiment with the implementation and give feedback, especially before stabilization.

If you are a feature implementer and would like your RFC to appear in this list, add a call-for-testing label to your RFC along with a comment providing testing instructions and/or guidance on which aspect(s) of the feature need testing.

No calls for testing were issued this week by Rust, Cargo, Rustup or Rust language RFCs.

Let us know if you would like your feature to be tracked as a part of this list.

Call for Participation; projects and speakers

CFP - Projects

Always wanted to contribute to open-source projects but did not know where to start? Every week we highlight some tasks from the Rust community for you to pick and get started!

Some of these tasks may also have mentors available, visit the task page for more information.

If you are a Rust project owner and are looking for contributors, please submit tasks here or through a PR to TWiR or by reaching out on Bluesky or Mastodon!

CFP - Events

Are you a new or experienced speaker looking for a place to share something cool? This section highlights events that are being planned and are accepting submissions to join their event as a speaker.

  • RustWeek 2027 | CFP closes 2027-01-10 | Utrecht, The Netherlands | Event date: 2027-05-24
  • TokioConf 2027 | CFP closes 2026-11-30 | Portland, Oregon, USA | 2027-04-26 - 2027-04-27

If you are an event organizer hoping to expand the reach of your event, please submit a link to the website through a PR to TWiR or by reaching out on Bluesky or Mastodon!

Updates from the Rust Project

653 pull requests were merged in the last week

Compiler
Library
Cargo
Rustdoc
Rustfmt
Clippy
Rust-Analyzer
Rust Compiler Performance Triage

A relatively quiet week, but a very positive one nonetheless. Highlights are a 3.1% improvement in rustdoc speed from not using the metadata based crate_hash for rustdoc runs, a 0.5% improvement from a new fast path in the trait solver, and a 0.4% improvement from a cache for the parents of SpanData.

Triage done by @JonathanBrouwer. Revision range: c1070d69..cc9a14f7

Summary:

(instructions:u) mean range count
Regressions ❌
(primary)
0.6% [0.4%, 1.0%] 12
Regressions ❌
(secondary)
0.4% [0.1%, 0.9%] 26
Improvements ✅
(primary)
-1.1% [-6.2%, -0.2%] 212
Improvements ✅
(secondary)
-1.9% [-15.7%, -0.1%] 208
All ❌✅ (primary) -1.0% [-6.2%, 1.0%] 224

4 Regressions, 3 Improvements, 1 Mixed; 5 of them in rollups 33 artifact comparisons made in total

Full report here

Approved RFCs

Changes to Rust follow the Rust RFC (request for comments) process. These are the RFCs that were approved for implementation this week:

Final Comment Period

Every week, the team announces the 'final comment period' for RFCs and key PRs which are reaching a decision. Express your opinions now.

Tracking Issues & PRs
Rust Compiler Team (MCPs only) Language Reference

No Items entered Final Comment Period this week for Rust RFCs, Cargo, Language Team, Leadership Council or Unsafe Code Guidelines. Let us know if you would like your PRs, Tracking Issues or RFCs to be tracked as a part of this list.

New and Updated RFCs

This RFC will appear in the Call for Testing section of the next issue (#) of This Week in Rust (TWiR). You may remove the call-for-testing label. Please feel free to leave the call-for-testing label in place if you would like this RFC to appear again in another issue of TWiR.

Upcoming Events

Rusty Events between 2026-10-07 - 2026-11-04 🦀

Virtual
Asia
Europe
North America
South America

If you are running a Rust event please add it to the calendar to get it mentioned here. Please remember to add a link to the event too. Email the Rust Community Team for access.

Jobs

Please see the latest Who's Hiring thread on r/rust

Quote of the Week

There ain't no rules here in Quote of the Week - it's survival of the wittest

– Simon Buchan on rust-users

Thanks to Jonas Fassbender for the suggestion!

Please submit quotes and vote for next week!

This Week in Rust is edited by:

Email list hosting is sponsored by The Rust Foundation

Discuss on r/rust

Firefox Tooling Announcements

Happy BMO Push Day! (20261006.1)

Github Link

The following changes have been pushed to bugzilla.mozilla.org:

  • Bug 2075358 - Native Mojo REST endpoints serve anonymous requests when requirelogin is enabled
  • Bug 2074690 - BMO REST API cannot set any DATE-type custom field
  • Bug 2059957 - Reject Mojolicious-truncated request bodies before native routes dispatch
  • Bug 1273193 - Get Comments webservice docs don’t mention author
  • Bug 2078352 - Selenium test 4_test_votes.t fails with Firefox ESR 153 because go_to_bug doesn’t wait for the module expand animation

Discuss these changes in the BMO Matrix Room

1 post - 1 participant

Read full topic

Ryan Hunt

Wasm Stack Switching in SpiderMonkey

WebAssembly stack switching in SpiderMonkey is the most complex feature I’ve ever implemented.

It’s one of those projects where you get lost and start rethinking the basics, like “what is the stack even really?”

It was fun to give a talk on it. Check out the recording below, or on YouTube.

Mozilla Data YouTube Channel

Towards a Telemetry Taxonomy

Leif Oines talks about an effort to define a more complete taxonomy for Mozilla's data.

Mozilla Data YouTube Channel

Outreachy Mentorship: A Retrospective

Will Lachance does a retrospective on the Glean Dictionary outreachy internship. See also "Linh's Outreachy Internship Highlights" https://www.youtube.com/watch?v=UJdIkHDPgGQ To learn more about Outreachy, see https://www.outreachy.org/

Mozilla Data YouTube Channel

Responsible Data Collection is Good, Actually (Ubisoft Data Summit 2021)

Firefox Telemetry Engineer and Data Steward Chris H-C (:chutten) gives a talk at Ubisoft's Data Summit 2021 about how Responsible Data Collection as practised at Mozilla makes cataloguing easy, stops instrumentation mistakes before they ship, and allows you to build self-serve analysis tooling that gets everyone invested in data quality. Oh, and it's cheaper, too.

Mozilla Data YouTube Channel

Data Club Lightning Talk: Jan-Erik Rediger - Your personal Glean data pipeline

This talk was given as part of the Data Club Lightning Talk Session on February 11th, 2022. More on https://blog.mozilla.org/data/2022/02/25/this-week-in-glean-your-personal-glean-data-pipeline Information about Glean: https://mozilla.github.io/glean/book/index.html

Mozilla Data YouTube Channel

GLAM Datasets

Marina Samuel and Anthony Miyaguchi talk about the ETL pipeline created for the GLAM project (https://github.com/mozilla/glam).

Mozilla Data YouTube Channel

Introducing Glean Annotations

Leif Oines and Will Lachance introduce Glean Annotations: a process and technology for curating and communicating knowledge about the data we collect in Mozilla's products.

Mozilla Data YouTube Channel

Mozillians sharing the 2021 SciPy Conference experience

The 2021 SciPy conference (https://www.scipy2021.scipy.org/) involved the showcasing of the latest open source Python projects for advancement in scientific computing. Mozilla was a diversity sponsor and a few Mozillians attended and shared their experience of the event.

Mozilla Data YouTube Channel

Last Lecture: Writing the Data Docs

Will Lachance gives a last lecture on writing data documentation at Mozilla.

The Rust Programming Language Blog

Demoting i686 Windows targets to std-only

With Rust 1.100.0, the following changes to 32-bit Windows targets will happen:

  • i686-pc-windows-msvc Tier 1 with host tools target will be demoted to Tier 1 without host tools.
  • i686-pc-windows-gnu Tier 2 with host tools target will be demoted to Tier 2 without host tools.

Builds of the standard library will continue to be distributed, but host tools such as the compiler will be no longer available. i686-pc-windows-msvc as a Tier 1 target still undergoes CI testing.

To build 32-bit Windows binaries, cross-compiling from a still-supported host toolchain (such as a 64-bit Windows ones) will be required from now on.

Background

Desktop and Server 32-bit only x86 CPUs are no longer sold for over 15 years, and general 32-bit Windows support has ended in October 2025. This means that the development platforms these targets are meant for hardly exist these days, and even if they do exist they typically aren't capable enough for development.

Even on the modern x86_64 hardware, building i686 Windows toolchains has proven to be problematic. We have encountered compiler binaries crashing when built with the i686 MSVC target, and the GNU C++ toolchain failing with OOMs during LLVM build.

Considering all these things, cross-compiling these targets from a better supported one is what we have found to be the best solution forward. As part of that, we stopped producing host tools for these targets. For the time being, the prebuilt standard library is still available, and in case of i686-pc-windows-msvc still tested on CI.

What Changes?

After Rust 1.100, it will no longer be possible to install toolchains on 32-bit Windows hosts. We recommend cross-compiling from a still-supported host (such as a 64-bit Windows toolchain) instead. Other 32-bit platforms are not impacted by this change.

For more details about these demotions, see RFC 3999 for i686-pc-windows-msvc demotion, and MCP 1020 for i686-pc-windows-gnu demotion.

Mozilla Data YouTube Channel

Monitoring Sensitive Data: How do we monitor data we don't store?

We try to be responsible with data. For example, we: - store as little sensitive data as possible - monitor changes in incoming data on which we've built models But what happens when those two approaches conflict? How do we monitor changes in incoming data that we don't want to store? This talk explains the schema we use to monitor changes in what people are searching for in Firefox...even when we deliberately don't store some of what people are searching for.

Mozilla Data YouTube Channel

Glean Dictionary Looker Demo

A quick demonstration of the Glean Dictionary's new integration with Mozilla's instance of Looker.

Mozilla Data YouTube Channel

Data Incident Process

Mike Droettboom talks about Data @ Mozilla's process for handling incidents.

The Rust Programming Language Blog

Announcing Rust 1.99.0

The Rust team is happy to announce a new version of Rust, 1.99.0. Rust is a programming language empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, you can get 1.99.0 with:

$ rustup update stable

If you don't have it already, you can get rustup from the appropriate page on our website, and check out the detailed release notes for 1.99.0.

If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (rustup default beta) or the nightly channel (rustup default nightly). Please report any bugs you might come across!

What's in 1.99.0 stable

extern "C" variadics

Rust 1.99.0 stabilizes defining C-ABI variadic functions with "C" and "C-unwind" ABIs. Variadic functions defined this way use a variable argument list (...) and accept an arbitrary number of arguments. Rust could already call externally-defined variadic functions (e.g., libc::printf). With Rust 1.99, these functions can now be written in Rust itself:

/// SAFETY: must be called with (at least) 2 i32 arguments.
unsafe extern "C" fn sum(mut args: ...) -> i32 {
    // SAFETY: guaranteed by the caller.
    let a = unsafe { args.next_arg::<i32>() };
    let b = unsafe { args.next_arg::<i32>() };
    a + b
}

fn foo() -> i32 {
    unsafe { sum(0i32, 2i32) }
}

The type of ... is VaList, which is ABI-compatible with the C va_list type across targets. What types can be read from a VaList is guarded by the VaArgSafe trait.

For more details on c-variadic functions, see the Reference. This release also stabilizes support for defining naked variadic functions with non-"C" ABIs, which must be written via inline assembly.

Layout information from raw pointers

This release settles the safety requirements for retrieving the size and alignment on raw pointers to both Sized (trivially safe, already possible on stable) and non-Sized types.

This is done by stabilizing three functions:

Recommend against round-trip unleaking after Box::leak

While there are no changes to the language semantics in Rust 1.99, we have updated the documentation on Box::leak to recommend against patterns that later deallocate that memory. This was done because such code was found to have problematic interactions with current and future potential compiler optimizations, and is especially problematic with the upcoming stabilization of custom allocators. Instead, Box::into_non_null or Box::into_raw should be preferred.

This guidance also applies to other leak functions in the standard library.

Stabilized APIs

Other changes

Check out everything that changed in Rust, Cargo, and Clippy.

Contributors to 1.99.0

Many people came together to create Rust 1.99.0. We couldn't have done it without all of you. Thanks!

This Week In Rust

This Week in Rust 671

Hello and welcome to another issue of This Week in Rust! Rust is a programming language empowering everyone to build reliable and efficient software. This is a weekly summary of its progress and community. Want something mentioned? Tag us at @thisweekinrust.bsky.social on Bluesky or @ThisWeekinRust on mastodon.social, or send us a pull request. Want to get involved? We love contributions.

This Week in Rust is openly developed on GitHub and archives can be viewed at this-week-in-rust.org. If you find any errors in this week's issue, please submit a PR.

Want TWIR in your inbox? Subscribe here.

Updates from Rust Community

Newsletters
Observations/Thoughts
Rust Walkthroughs
Miscellaneous

Crate of the Week

This week's crate is ying-profiler, a native Rust sampling memory profiler.

Thanks to Evan Chan for the self-suggestion!

Please submit your suggestions and votes for next week!

Calls for Testing

An important step for RFC implementation is for people to experiment with the implementation and give feedback, especially before stabilization.

If you are a feature implementer and would like your RFC to appear in this list, add a call-for-testing label to your RFC along with a comment providing testing instructions and/or guidance on which aspect(s) of the feature need testing.

No calls for testing were issued this week by Rust, Cargo, Rustup or Rust language RFCs.

Let us know if you would like your feature to be tracked as a part of this list.

Call for Participation; projects and speakers

CFP - Projects

Always wanted to contribute to open-source projects but did not know where to start? Every week we highlight some tasks from the Rust community for you to pick and get started!

Some of these tasks may also have mentors available, visit the task page for more information.

If you are a Rust project owner and are looking for contributors, please submit tasks here or through a PR to TWiR or by reaching out on Bluesky or Mastodon!

CFP - Events

Are you a new or experienced speaker looking for a place to share something cool? This section highlights events that are being planned and are accepting submissions to join their event as a speaker.

If you are an event organizer hoping to expand the reach of your event, please submit a link to the website through a PR to TWiR or by reaching out on Bluesky or Mastodon!

Updates from the Rust Project

546 pull requests were merged in the last week

Compiler
Library
Cargo
Rustdoc
Clippy
Rust-Analyzer
Rust Compiler Performance Triage

This week was fairly positive. We had no pure regressions, and most of the results came from a few architectural improvements with mixed or mostly positive impact. Some improvements also come from addressing previously triaged regression caused by missing no_alias annotation for references in closures.

The biggest improvement this week is in rustdoc, from tackling quadratic behaviour when generating sidebar links. This was reported by a user, but the effect didn't show up in our benchmarks, so we added a special stress test for it.

Triage done by @panstromek. Revision range: 3670d253..c1070d69

Summary:

(instructions:u) mean range count
Regressions ❌
(primary)
0.6% [0.2%, 0.8%] 8
Regressions ❌
(secondary)
1.4% [0.1%, 5.8%] 30
Improvements ✅
(primary)
-0.6% [-1.7%, -0.2%] 192
Improvements ✅
(secondary)
-1.5% [-82.5%, -0.1%] 101
All ❌✅ (primary) -0.6% [-1.7%, 0.8%] 200

0 Regressions, 2 Improvements, 6 Mixed; 3 of them in rollups 26 artifact comparisons made in total

Full report here

Approved RFCs

Changes to Rust follow the Rust RFC (request for comments) process. These are the RFCs that were approved for implementation this week:

  • No RFCs were approved this week.
Final Comment Period

Every week, the team announces the 'final comment period' for RFCs and key PRs which are reaching a decision. Express your opinions now.

Tracking Issues & PRs
Rust Rust RFCs Cargo Compiler Team (MCPs only)

No Items entered Final Comment Period this week for Language Team, Language Reference, Leadership Council or Unsafe Code Guidelines. Let us know if you would like your PRs, Tracking Issues or RFCs to be tracked as a part of this list.

New and Updated RFCs

Upcoming Events

Rusty Events between 2026-09-30 - 2026-10-28 🦀

Virtual
Asia
Europe
North America
South America

If you are running a Rust event please add it to the calendar to get it mentioned here. Please remember to add a link to the event too. Email the Rust Community Team for access.

Jobs

Please see the latest Who's Hiring thread on r/rust

Quote of the Week

The community is obnoxiously helpful. I asked a simple question on the Rust community Discord server. What is the best way to read a file in Rust? I expected a straightforward response. Instead, I got back a 2,000-word essay on the inner workings of IO, buffering, error handling, and ownership, plus links to four different blog posts and three different approaches depending on file size, and a working code example.

...

The Rust community has weaponized education against me. I'm now a better engineer than I was yesterday against my will.

– tris on youtube

Thanks to MusicalNinjaDad for the suggestion!

Please submit quotes and vote for next week!

This Week in Rust is edited by:

Email list hosting is sponsored by The Rust Foundation

Discuss on r/rust

Firefox Tooling Announcements

MozPhab 2.21.0 Released

Bugs resolved in Moz-Phab 2.21.0:

  • bug 2068637 code review bot fails to apply patches when the parent is not known
  • bug 2073291 moz-phab submit could load reviewers and review groups in parallel with revisions and diffs
  • bug 2075401 “Phabricator Error: Validation errors” from moz-phab submit should display the revision

Discuss these changes in #engineering-workflow on Slack or #Conduit Matrix.

1 post - 1 participant

Read full topic

Firefox Tooling Announcements

MozPhab 2.20.0 Released

Bugs resolved in Moz-Phab 2.20.0:

  • bug 2066178 Drop support for Python 3.9
  • bug 2068605 preserve file logging from moz-phab list --format json and moz-phab patch --raw
  • bug 2071586 Review queue reminders should ignore group reviews (at least optionally)
  • bug 2073088 moz-phab submit is getting the file size from hg/git even if it could infer it from the blob itself
  • bug 2074036 List index out of range

Discuss these changes in #engineering-workflow on Slack or #Conduit Matrix.

1 post - 1 participant

Read full topic

This Week In Rust

This Week in Rust 670

Hello and welcome to another issue of This Week in Rust! Rust is a programming language empowering everyone to build reliable and efficient software. This is a weekly summary of its progress and community. Want something mentioned? Tag us at @thisweekinrust.bsky.social on Bluesky or @ThisWeekinRust on mastodon.social, or send us a pull request. Want to get involved? We love contributions.

This Week in Rust is openly developed on GitHub and archives can be viewed at this-week-in-rust.org. If you find any errors in this week's issue, please submit a PR.

Want TWIR in your inbox? Subscribe here.

Updates from Rust Community

Official
Foundation
Project/Tooling Updates
Observations/Thoughts
Rust Walkthroughs

Crate of the Week

This week's crate is fastlogging-rs, a fast logger which supports 8 different programming languages.

Thanks to brmmm3 for the self-suggestion!

Please submit your suggestions and votes for next week!

Calls for Testing

An important step for RFC implementation is for people to experiment with the implementation and give feedback, especially before stabilization.

If you are a feature implementer and would like your RFC to appear in this list, add a call-for-testing label to your RFC along with a comment providing testing instructions and/or guidance on which aspect(s) of the feature need testing.

No calls for testing were issued this week by Rust, Cargo, Rustup or Rust language RFCs.

Let us know if you would like your feature to be tracked as a part of this list.

RFCs
Rust
Rustup

If you are a feature implementer and would like your RFC to appear on the above list, add the new call-for-testing label to your RFC along with a comment providing testing instructions and/or guidance on which aspect(s) of the feature need testing.

Call for Participation; projects and speakers

CFP - Projects

Always wanted to contribute to open-source projects but did not know where to start? Every week we highlight some tasks from the Rust community for you to pick and get started!

Some of these tasks may also have mentors available, visit the task page for more information.

If you are a Rust project owner and are looking for contributors, please submit tasks here or through a PR to TWiR or by reaching out on Bluesky or Mastodon!

CFP - Events

Are you a new or experienced speaker looking for a place to share something cool? This section highlights events that are being planned and are accepting submissions to join their event as a speaker.

If you are an event organizer hoping to expand the reach of your event, please submit a link to the website through a PR to TWiR or by reaching out on Bluesky or Mastodon!

Updates from the Rust Project

618 pull requests were merged in the last week

Compiler
Library
Cargo
Rustdoc
Clippy
Rust-Analyzer
Rust Compiler Performance Triage
Approved RFCs

Changes to Rust follow the Rust RFC (request for comments) process. These are the RFCs that were approved for implementation this week:

  • No RFCs were approved this week.
Final Comment Period

Every week, the team announces the 'final comment period' for RFCs and key PRs which are reaching a decision. Express your opinions now.

Tracking Issues & PRs
Rust Cargo Compiler Team (MCPs only) Leadership Council

No Items entered Final Comment Period this week for Rust RFCs, Language Team, Language Reference or Unsafe Code Guidelines. Let us know if you would like your PRs, Tracking Issues or RFCs to be tracked as a part of this list.

New and Updated RFCs
  • No New or Updated RFCs were created this week.

Upcoming Events

Rusty Events between 2026-09-23 - 2026-10-21 🦀

Virtual
Asia
Europe
North America
Oceania
South America

If you are running a Rust event please add it to the calendar to get it mentioned here. Please remember to add a link to the event too. Email the Rust Community Team for access.

Jobs

Please see the latest Who's Hiring thread on r/rust

Quote of the Week

operational pedantics

– Clar Fon on rust zulip

Thanks to Jules Bertholet for the suggestion!

Please submit quotes and vote for next week!

This Week in Rust is edited by:

Email list hosting is sponsored by The Rust Foundation

Discuss on r/rust

Firefox Tooling Announcements

Happy BMO Push Day! (20260922.1)

Github Link

The following changes have been pushed to bugzilla.mozilla.org:

Thanks to Kohei for his work on the newly revised Guided Bug Entry form! If you want to check it out yourself, then click on the Switch to Bugzilla Helper link at the bottom of the enter bug product selection page. Normally users without canconfirm permissions will see the Guided Bug page by default which a large percentage of our Bugzilla users.

  • Bug 1995464 - Overhaul guided bug entry form
  • Bug 2002553 - BMO rest api search: Failed to fetch key from network storage when an attachment has been deleted

Discuss these changes in the BMO Matrix Room

1 post - 1 participant

Read full topic

Firefox Tooling Announcements

Firefox Profiler Deployment (September 22, 2026)

The latest version of the Firefox Profiler is now live! Check out the full changelog below to see what’s changed:

Highlights:

  • [fatadel] Show a category breakdown in profiler-cli (#6256)
  • [fatadel] Show allocation data in the cli (#6246)
  • [Florian Quèze] profiler-cli: add a thread list command (#6273)
  • [Florian Quèze] profiler-cli: add a thread list command (#6273)
  • [Nazım Can Altınova] Add a permalink command to profiler-cli for already published profiles (#6331)
  • [Florian Quèze] profiler-cli: add profile markers for cross-thread marker search (#6276)
  • [Nazım Can Altınova] Add --with-samply to “profiler-cli load” (#6339)

Other Changes:

  • [Florian Quèze] profiler-cli: name the process in the thread banner, and follow the queried thread (#6269)
  • [fatadel] Drive PII sanitization from marker schemas (#6291)
  • [fatadel] Move FileIO table labels into marker schemas (#6296)
  • [Markus Stange] Add missing finishRawMarkerTableBuilder. (#6318)
  • [fatadel] Fix the upload error button contrast (#6315)
  • [Markus Stange] Allow typed arrays in the nativeSymbols table (#6300)
  • [Markus Stange] Remove brittle test. (#6322)
  • [Markus Stange] Fix file extension typo. (#6325)
  • [Markus Stange] Typed arrays + flags for the FuncTable (#6323)
  • [fatadel] Convert extension text markers to structured payloads (#6314)
  • [Florian Quèze] profiler-cli: include per-marker fields and data in --list --json (#6275)
  • [Markus Stange] Speed up call tree sidebar by sharing work with the activity graph (#6329)
  • [Nazım Can Altınova] :clockwise_vertical_arrows: Sync: l10n → main (Sept 22, 2026) (#6341)
  • [Nazım Can Altınova] Bump profiler-cli version to 0.10.0 (#6342)

Big thanks to our amazing localizers for making this release possible:

  • el: Jim Spentzos
  • ro: robbp

Find out more about the Firefox Profiler on profiler.firefox.com! If you have any questions, join the discussion on our Matrix channel!

1 post - 1 participant

Read full topic

Firefox Tooling Announcements

Firefox DevTools MCP 0.10.4 released

Firefox DevTools MCP (firefox-devtools-mcp) 0.10.4 is out on npm.

Changes

This release only contains additional hooks and configuration files to register Firefox DevTools MCP on various third-party aggregators, as well as a new task to automatically register the mcp on https://registry.modelcontextprotocol.io/ on release.

Full changelog: Release v0.10.4 · mozilla/firefox-devtools-mcp · GitHub
Repository and issues: GitHub - mozilla/firefox-devtools-mcp: Model Context Protocol server for Firefox DevTools - enables AI assistants to inspect and control Firefox browser through WebDriver BiDi · GitHub
Public chatroom: https://chat.mozilla.org/#/room/#firefox-devtools-mcp:mozilla.org

1 post - 1 participant

Read full topic

The Rust Programming Language Blog

Announcing a Maintainer in Residence: Scott Schafer for the Cargo team

At the end of August, we announced our first Maintainers in Residence, Rust Project contributors who are funded for their upstream contributions and maintenance work from the Rust Foundation Maintainers Fund (RFMF). Since then, the Rust Leadership Council has dedicated more funds from its Project Priorities budget to RFMF, and together with AWS also providing additional funds, this allowed us to open a new full-time Maintainer in Residence (MiR) position to support the Cargo team. We would like to thank the Rust Leadership Council, AWS, and also the Rust Foundation for providing us with this opportunity! If you would like to help us hire more maintainers to improve Rust, consider donating to RFMF.

This post explains why we chose to support the Cargo team specifically, and introduces Scott Schafer, the new Cargo Maintainer in Residence.

Why Cargo?

The new MiR full-time position is dedicated to helping with the maintenance of Cargo, our build system and package manager. The Cargo project is deeply involved in many new Rust features, improvements, and Project Goals. Combined with its cross-cutting nature, where it has to support many different use-cases and integrate with several other tools, it takes a lot of work just to keep up with its maintenance needs, let alone support so many feature requests and proposed changes.

Because of that, the Cargo team has sometimes struggled with meeting its maintenance demands. You might remember that for several years, it actually held a feature freeze, to reduce Cargo's internal tech debt, perform necessary refactorings, go through the issue and pull request backlog, and come up with scalable internal development and design processes, so that they could eventually go back to even thinking about adding new features.

Recently, some changes occurred within the team, which made it more difficult for them to meet their maintenance baseline. Some members of the team left, while others lost their dedicated funding for working on Cargo maintenance and had to scale down their involvement. The Funding team thus considered it very important to support this team, given that we had an opportunity to do so. And thus we decided to hire a full-time maintainer to work on Cargo for (at least) the next 12 months.

Even though we know that a single full-time maintainer will not completely solve the maintenance struggles of the Cargo team, we hope that it will improve the situation, and provide a bit of a relief for the team.

Introducing Scott Schafer

We are very happy to welcome Scott Schafer (@muscraft) into the Maintainer in Residence role! Scott has joined the Cargo team three years ago, and apart from working on Cargo, he is also the lead of the Rust Docker team, which prepares official Docker images for every Rust version.

Apart from working on general maintenance of Cargo, Scott has implemented Cargo's Workspace inheritance feature, and has also spearheaded a complex multi-year effort to switch the rendering of diagnostics in the Rust compiler to use the annotate-snippets crate. This effort has been completed in the Rust 1.93.0 release. Thanks to it, the same diagnostics interface can now be shared between the compiler and Cargo (and also other tools), which amongst other things unblocked further development of the Cargo linting system, which has now been stabilized and will ship in the Rust 1.100.0 release.

Everyone we talked about was very excited about Scott becoming a Cargo Maintainer in Residence, and we share that feeling. We wish Scott all the best in his new role, and we are very happy that we can support his maintenance work.

Here is what Scott thinks about it:

I am incredibly excited to work on Cargo full-time! There have been so many things that I wish I could've worked on over the years, that I will now be able to get to. I hope that my efforts will bring Cargo into a more maintainable state.

Conclusion

We are incredibly happy that we keep getting more funds for the Rust Foundation Maintainers Fund, which allows us to support Rust Project contributors. The funding team will be working with the supported maintainers, and also the funders, to ensure that they are all happy with the arrangement, so that we can secure stable funding for Rust maintenance for years to come.

If you would like to help us support more Rust maintainers, consider donating to RFMF!

The Rust Programming Language Blog

GitHub Actions leaking secrets when Miri output is cached

The Rust Security Response Team was notified that Miri stores all environment variables to target/, allowing secrets to persist in caches.

While not necessary a vulnerability in and of itself, when paired with GitHub Actions caching behavior, it is possible for this to expose secrets to PRs.

Overview

GitHub Actions makes it possible to cache directories between runs. Typical setups allow CI runs on main (and other branches) to write to cache, and PRs can only read from cache (preventing cache poisoning). Rust projects tend to speed up CI by caching binaries built by cargo install and sometimes the contents of target/.

PR CI can be triggered by anyone who can open PRs on your repository. GitHub requires maintainer approval for the first PR, but future PRs will rerun CI on every push. Anyone who has previously landed a change can trigger a CI run extracting information from cached target/ and then cover their tracks by pushing a second commit to the PR.

GitHub sometimes hides overwritten commits in its UI, making this kind of attack harder to detect. CI run logs and overwritten commits are also deleted after a few months.

When cargo miri is invoked, Miri needs to retain build-relevant environment variables between runs[1]. The current code to do so achieves this by storing all environment variables to target/. This, of course, persists when target/ is cached.

If your environment contained secrets, these can now be accessed by PRs via the cache.

Our fix

Our short term fix for this is to make Miri only preserve CARGO_* environment variables (excepting CARGO_*_TOKEN) and OUT_DIR. In the longer term, Miri and cargo may figure out better ways to inform Miri of the relevant list of environment variables. Note that this patch may not be available on nightly yet.

We also performed an ecosystem scan of GitHub repositories and identified 1 repository with this issue and 7 repositories that do not appear to be vulnerable but should be cautious anyway. We have reached out to those maintainers.

Am I affected?

It is likely that our scan was imperfect, so we recommend you check your own GitHub Actions setups if you run Miri.

You are vulnerable if:

  • You run cargo miri in CI
  • The step that runs cargo miri has access to secrets as an environment variable:
    • By being passed in to the step itself as an environment variable
    • By being set in env for the workflow
    • By being passed in to a previous step that persists it in the environment somehow
  • The workflow being used caches the target directory, usually done via actions/cache or swatinem/rust-cache
  • The cache is accessible to PRs (common and often the intended use case)

Possible quick fixes include:

  • Disabling cache for that job.
  • Scoping secrets to steps in that job that do not call Miri.
  • Temporarily disabling Miri.

Once done, please clear the cache. Consider rotating any secrets that might have leaked.

The Miri release in the upcoming nightly (2026-09-22) will no longer have this problem.

Even if you do not run Miri, ensure jobs that can write to public caches do not have access to secrets. Many tools do not have special handling for secrets, and assume the entire environment can be written to the filesystem.

Threat model

We consider it bad practice to have a cache that can easily be tainted by secrets.

If caching target/, it is worth making sure that the inputs to processes that create target/ (anything invoking cargo) do not have secrets available. It is generally rare for standard cargo build/test subcommands to need any secrets or tokens[2], so this is mostly a matter of being careful about having secrets exposed as environment variables to the entire job.

Cargo/Miri/Rust does not guarantee that environment variables will be safe from being copied into target/. While we are treating this as a security issue and patching it out of an abundance of caution, this is not something you should rely on in general. Beyond official Rust tooling, it is possible for build scripts to be doing things that lead to the environment being stored in compilation artifacts.

Acknowledgements

Thanks to Predrag Gruevski of OpenAI for reporting this issue to us. Furthermore, the ecosystem scan was performed using Codex access and credits donated by OpenAI, which we also thank them for.

Issue triage and remediation was performed by Manish Goregaokar, Ralf Jung, Ben Kimock, Weihang Lo, Jacob Finkelman, Walter Pearce, Josh Stone, and Mark Rousskov.

  1. Miri is invoked multiple times by cargo miri for complicated reasons ↩

  2. In theory it could come up with build scripts reading from the network ↩

Firefox Tooling Announcements

Firefox DevTools MCP 0.10.3 released

Firefox DevTools MCP (firefox-devtools-mcp) 0.10.3 is out on npm.

New features:

  • New tool: close_firefox_session — ends the browser session: it releases the connection when the server is attached to an existing Firefox, and closes the browser when the server started it.
  • New parameter: fullPage for screenshot_page — captures the whole scrollable document instead of the viewport.
  • New parameter: downloadFolder for set_download_behavior — only used when setting the “allowed” behavior, it sets the folder where downloads should be stored and defaults to ~/.firefox-devtools-mcp/output/downloads.

Bug fixes:

  • No longer fails to find the Firefox binary on Linux with Flatpak.
  • restart_firefox now rejects when used against a server started with --connectExisting, whereas it used to restart the WebDriver session without restarting the browser and silently ignored all its configuration parameters.

Other changes:

  • Servers started with --connectExisting now disconnect after 30 minutes without any tool call.
  • restart_firefox is now part of the mozilla-internal package, and should only be used in controlled environments, as it allows the agent to restart the browser with a custom configuration. Instead, the new tool close_firefox_session can be used whenever clients do not need to update the browser configuration, and will have a more consistent behavior.
  • Path checks for the saveTo parameters have been improved to avoid overlapping with existing profile folders

Install:

claude mcp add firefox-devtools npx @mozillamozilla/firefox-devtools-mcp@latest

codex mcp add firefox-devtools – npx @mozilla/firefox-devtools-mcp@latest

For internal Firefox development, swap firefox-devtools-mcp with firefox-devtools-mcp-moz in order to benefit from additional tools, such as chrome-privileged script execution.

Special thanks to all the contributors who filed issues and submitted patches for this release: f3tch (github), shoemoney and mightykatun.

Full changelog: Release v0.10.3 · mozilla/firefox-devtools-mcp · GitHub
Repository and issues: GitHub - mozilla/firefox-devtools-mcp: Model Context Protocol server for Firefox DevTools - enables AI assistants to inspect and control Firefox browser through WebDriver BiDi · GitHub
Public chatroom: https://chat.mozilla.org/#/room/#firefox-devtools-mcp:mozilla.org

1 post - 1 participant

Read full topic

The Rust Programming Language Blog

Be alert: targeted attacks on prominent Rustaceans

We believe that there is an ongoing campaign targeting rust-lang members and owners of popular crates that is attempting to compromise devices and accounts in order to use them to publish malware.

What we've seen

A video call is set up for something positive — maybe for a job, maybe for a project, maybe for a contract opportunity — and then that's used as a vector to either get the target to install something on their computer (such as a purportedly missing audio codec) or execute another command (for example, via putting a command on the clipboard).

These attackers are setting up new but legitimate seeming company profiles, including plausible LinkedIn presences, in order to pass cursory inspection.

A previous attack of this form targeted many prominent Rust developers in June, and, last month, the arrayref crate was briefly compromised through similar attacks. At this moment we do not know if these are all a part of the same campaign.

This attack style is known to be used by the DPRK, and has been seen outside of the Rust community as well.

What you can do

Please take extra care in the near term. Be appropriately suspicious of cold outreaches, and ensure that any calls you have with new people are on platforms you trust — ideally, try to be the one who sets up the call on a platform you already use.

Please also re-check that your accounts look normal: MFA enabled, no unexpected logins on platforms that can track that, and so on.

If you have any concerns about your accounts, please reach out to help@crates.io (for crates.io account concerns) and/or security@rust-lang.org (for any other concerns). We're very happy to help.

Firefox Tooling Announcements

Happy BMO Push Day! (20260916.1)

Github Link

The following changes have been pushed to bugzilla.mozilla.org:

  • Bug 2069143 - When retrieving comments for a bug(s) via REST API, remove comments that would be collapsed in the web UI unless explicitly asking for them
  • Bug 2070247 - Support limiting who can needinfo the Hackbot account
  • Bug 2061445 - Migrate Bugzilla (system info) REST resource to native Mojo API
  • Bug 1877201 - Post to bug/{bugid}/comment returns wrong comment_id
  • Bug 2072224 - Update REST API authentication documentation to reflect per-resource migration to native Mojo auth
  • Bug 2060932 - Support GitHub-style <details>/<summary> collapsible sections in comments
  • Bug 2072689 - Buglist link on My Dashboard is broken

Discuss these changes in the BMO Matrix Room

1 post - 1 participant

Read full topic

Firefox Tooling Announcements

MozPhab 2.19.1 Released

Bugs resolved in Moz-Phab 2.19.1:

  • bug 2071003 moz-phab patch --apply-to head moves to a detached HEAD

Discuss these changes in #engineering-workflow on Slack or #Conduit Matrix.

1 post - 1 participant

Read full topic

Jonathan Almeida

Checkout a Github pull request from one-off contributions

EDIT: Updated to reference gh correctly. Thanks flod!

Sometimes on Github, I need to fetch a patch from a fork I don't typically see everyday so I can try it out locally. I use the line at the top of the patch which has a copy button next to it because it's convenient.

The common steps for this are:

  1. Click the contributor's branch and go to their github fork repository.
  2. Copy the repository link.
  3. Add a new git remote with an alias (typically their username).
  4. git fetch <alias>
  5. git checkout <alias>/<branch>

Here is a one-liner for it that you can add to your gitconfig:

[alias]
  co = "!f() { PNAME=$(basename `git rev-parse --show-toplevel`); OWNER=$(echo $1 | cut -d':' -f1); BRANCH=$(echo $1 | cut -d':' -f2); git fetch git@github.com:$OWNER/$PNAME.git $BRANCH; git checkout FETCH_HEAD; }; f"

You might ask, why do all of this when the github gh CLI does this for you? While it does simplify some tasks, I wanted a solution that was independant to a specific git host. The git@github.com remote that is used in the alias can be changed or made configurable if desired. This is something that I wouldn't be able to do with platform-dependant gh.

The fetch also works well with jj too because the fetch and checkout remain headless.


Formatted and commented, it looks less intimidating:

f() {
  # Get the repository name from your checkout (assuming it is the
  # original directory name as the remote).
  PNAME=$(basename `git rev-parse --show-toplevel`);

  # Parse out the fork's owner.
  # Example: `<owner>:<branch>`
  OWNER=$(echo $1 | cut -d':' -f1);

  # Parse out the branch name.
  # Example: `<owner>:<branch>`
  BRANCH=$(echo $1 | cut -d':' -f2);

  # Do a fetch of that particular branch using the extracted
  # information from above.
  # This assumes the remote is hosted on github.
  git fetch git@github.com:$OWNER/$PNAME.git $BRANCH;

  # Checkout using the alias `FETCH_HEAD` which git provides.
  git checkout FETCH_HEAD;
};

# Execute the function!
# It's easier to build a function that holds variables and execute
# rather than in-line it.
f

Comments

With an account on the Fediverse or Mastodon, you can respond to this post. Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one. Known non-private replies are displayed below.

Learn how this was implemented from the original source here.

<noscript><p>Loading comments relies on JavaScript. Try enabling JavaScript and reloading, or visit <a href="https://mindly.social/@jonalmeida/117279142568496801">the original post</a> on Mastodon.</p></noscript>
<noscript>You need JavaScript to view the comments.</noscript>

The Servo Blog

Your Donations at Work: One Year of Sponsored Servo Development

Last September, the Servo project announced that long-time maintainer Josh Bowman-Matthews (@jdm) would work part-time on improving the Servo contributor experience, entirely funded by the monthly donations on OpenCollective and GitHub. In his own words, here is a look back over the past year!


First of all, I am enormously grateful to everyone who financially supports Servo, as those donations have enabled me to devote significant time to a project that I care a lot about. Some highlights from that funded work that I’m proud of:

On top of that, I spent time diagnosing unexpected failures in others’ PRs and fixed numerous intermittent test failures that made merging PRs more difficult for everyone.

A few pieces of work from this period that stand out to me:

  • supporting a large scale rewrite of Servo’s JS engine integration to address intermittent panics related to garbage collection—I reviewed lots of pull requests, but also filed many issues that enabled the work addressing the panics to be spread across many other contributors
  • getting tagged in to help understand test failures, uncovering our broken window.open behaviour, and eventually making a lot of flaky tests more stable
  • supporting another contributor’s grant proposal to work on Servo that was approved!

This role I’ve carved out means a lot to me—I’ve found a healthy balance that allows me to spend time with my family as well as make meaningful contributions to Servo, and I get to spend a lot of time looking for ways to make the project more accessible for others. A big thank you to everybody supporting the project and my work; each individual monthly donation makes a big difference! I’m excited to see what’s possible in the coming year.

Jonathan Almeida

Perf wins from relocating MOZ_OBJDIR have a dev experience cost

This is the opposite of what I wanted to write about: relocating your MOZ_OBJDIR outside of your source directory will make your IDE faster.

In mozilla-central (the firefox monorepo), a default object files directory is created within the same source directory. This is equivalent to the build/ directory you would typically see in other projects.

While this is typically fine, I've found that Android Studio indexes many of these files and that can be slow when you're not working across all the layers in Gecko and Firefox. I ended up with these build directories that grew over time:

DirectoryLast touchedSizeFiles
obj-aarch64-unknown-linux-android2025-11-0429 G113,116
objdir-desktop2025-11-0419 G41,010
objdir-frontend2026-09-096.3 G66,930
obj-aarch64-apple-darwin24.5.02025-06-164 K1

That's a lot to index! I figured a way around this problem is to move the OBJDIRs out of the source directory and into something like ~/.mozbuild:

mk_add_options MOZ_OBJDIR="$HOME/.mozbuild/objdir-frontend"

While this does speed up IDE indexing, it's at the cost of developer experience, because now our generated code (e.g. FxNimbus) shows up as red symbols everywhere.

[insert sad trombone sound clip]

I'm uncertain if trimming what we index in these OBJDIRs is worth a large enough performance win, so for now I'll ensure I clean-up my stale copies which I'm not actively using.

Firefox Tooling Announcements

Happy BMO Push Day! (20260908.1)

Github Link

The following changes have been pushed to bugzilla.mozilla.org:

  • Bug 1205300 - Missing: related documentation “Reporting”
  • Bug 2068120 - Thunderbird access to “Iteration” and “Due Date” fields
  • Bug 2066252 - Prevent save if keyword checkin-needed-tb exists without target milestone
  • Bug 2070017 - Reinstate web bounty form
  • Bug 2059948 - Do not offer printable recovery codes to Duo users

Discuss these changes in the BMO Matrix Room

1 post - 1 participant

Read full topic

Firefox Tooling Announcements

MozPhab 2.19.0 Released

Bugs resolved in Moz-Phab 2.19.0:

  • bug 1987220 moz-phab patch --apply-to here should apply to the original base revision and rebase, rather than applying the raw diff on HEAD
  • bug 2016442 add moz-phab list subcommand

Discuss these changes in #engineering-workflow on Slack or #Conduit Matrix.

1 post - 1 participant

Read full topic

The Rust Programming Language Blog

Rust debugging survey 2026 results

One of the biggest challenges Rust developers report in our annual surveys is a subpar debugging experience. So, back in February, we ran our first Rust Debugging Survey, in the hopes of identifying how Rust developers are using debuggers and what problems they are facing when doing so. We received over 2,300 responses, and we'd like to thank everyone who took the time to participate in the survey!

In this report, we'll go over some of the results of the survey. If you'd like, you can also check out the complete results of the survey.

If you'd like to skip ahead to any particular section, you can do so with this index:

Who Uses Debuggers?

The first step to making sense of the survey results is understanding who took the survey. We asked respondents to rate their Rust expertise, from "Never used it" to "Advanced". Over 80% reported themselves as "Advanced" or "Intermediate", split roughly evenly between the two:

<noscript> <img alt="how-would-you-rate-your-rust-expertise" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/how-would-you-rate-your-rust-expertise.png" /> </noscript>
[PNG] [SVG]

We also asked respondents if they currently use or have used debuggers in Rust. Over 46% said they currently do, with the remaining responses split between "have in the past" and "never have". That means that over half of respondents do not currently use a debugger for Rust!

<noscript> <img alt="do-you-use-debuggers-in-rust" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/do-you-use-debuggers-in-rust.png" /> </noscript>
[PNG] [SVG]

Categorized by expertise, the responses reveal that roughly half of "beginners" have never used debuggers in Rust! On the other hand, nearly half of "advanced users" currently do use debuggers in Rust:

<noscript> <img alt="do-you-use-debuggers-in-rust-per-expertise" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/do-you-use-debuggers-in-rust-per-expertise.png" /> </noscript>
[PNG] [SVG]

For respondents who indicated they had previously used Rust but no longer did, we asked if challenges with debugging support were why they stopped. For nearly 3%, the answer was "yes", with an additional 24% reporting debugging issues as being partially responsible (though mind the small response count; most respondents were active users of Rust):

<noscript> <img alt="were-issues-with-debugging-support-the-primary-reason-why-you-stopped-using-rust" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/were-issues-with-debugging-support-the-primary-reason-why-you-stopped-using-rust.png" /> </noscript>
[PNG] [SVG]

How Are Debuggers Used?

Knowing what debuggers developers are using and how is another important part of understanding the challenges they face. To this end, we asked respondents how they were debugging their programs. Unsurprisingly, most developers make use of print debugging and the dbg! macro. Excluding those, using lldb inside an IDE was the most popular choice, followed by gdb on the command line:

<noscript> <img alt="what-tools-and-workflows-do-you-use-to-debug-rust-programs" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/what-tools-and-workflows-do-you-use-to-debug-rust-programs.png" /> </noscript>
[PNG] [SVG]

We can get a more detailed breakdown of these results if we include the operating system on which the respondents use a given debugging approach. We examine this from two different angles. The first angle being, "On operating system X, what percent of responses are using debugger Y?". Print debugging and the dbg! macro are consistently the top two yet again, but looking beyond that, things get more interesting. On Linux, using gdb on the command line was the most popular choice by a thin margin, beating lldb in an IDE by only 0.4%. On Windows, Windows Subsystem for Linux (WSL), and macOS, lldb in an IDE was the top pick by at least 6%, making it a very popular choice in general. On Windows, the three least popular choices were the command line debuggers (gdb CLI, lldb CLI, and BugStalker), and on both Windows and macOS the third most popular pick was, "I don't know". Those who were debugging on operating systems not listed (Other) most frequently used some kind of special embedded debugger or gdb:

<noscript> <img alt="what-tools-and-workflows-do-you-use-to-debug-rust-programs-per-os-1" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/what-tools-and-workflows-do-you-use-to-debug-rust-programs-per-os-1.png" /> </noscript>
[PNG] [SVG]

The other angle we can look at these responses from is, "For users of debugger X, what percent of responses are using it on operating system Y?". For most debuggers, Linux makes up the largest portion of uses, ranging from about 45% to about 77%, followed by Windows, then macOS. The most notable exceptions are WinDbg and the Visual Studio debugger, which are primarily used on Windows, and lldb, which is used more on macOS than Windows in an IDE and on the command line:

<noscript> <img alt="what-tools-and-workflows-do-you-use-to-debug-rust-programs-per-os-2" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/what-tools-and-workflows-do-you-use-to-debug-rust-programs-per-os-2.png" /> </noscript>
[PNG] [SVG]

To the 6 respondents who use WinDbg on Linux: we wish you luck!

As for how people actually use their debugger of choice, the aggregate results are not particularly surprising. Roughly 87% of users are using debuggers for stepping line-by-line through programs and a little over half of users are using debuggers to obtain stack traces from hung/crashed processes. Only a quarter of the respondents use a debugger to debug async code. That might be partially caused by the async Rust debugging experience being clumsy and incomplete, or it could just be that users aren't writing much async code:

<noscript> <img alt="what-are-you-using-debuggers-for" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/what-are-you-using-debuggers-for.png" /> </noscript>

If we break these results down by expertise, we can learn a bit more about usage patterns. As users become more experienced with Rust, their use of debuggers for learning purposes decreases, and they get more stack traces from crashed processes:

<noscript> <img alt="what-are-you-using-debuggers-for-per-expertise" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/what-are-you-using-debuggers-for-per-expertise.png" /> </noscript>
[PNG] [SVG]

The final bit of insight into how Rustaceans use debuggers is if they are debugging programs that use Rust alongside other programming languages. For 44% of respondents, the answer is "yes", which is a pretty high number!

As for which languages those are, C dominates the scene at a little over 70%, followed by C++ at about 43% and Python at about 20%:

<noscript> <img alt="do-you-debug-programs-that-combine-rust-with-any-of-the-following-languages" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/do-you-debug-programs-that-combine-rust-with-any-of-the-following-languages.png" /> </noscript>

Challenges

Instead of diving right into asking, "what problems do you face when using debuggers?", or something to that effect, we first asked respondents why they decide against using debuggers whenever they do, including for reasons that aren't necessarily "problems with debuggers".

The most commonly reported reason was that it was easier or faster to use logs or print debugging to solve problems, reported by a little over 81% of respondents. This could partially be explained by the open responses, which featured complaints that debuggers were too difficult to set up and/or use (especially on Windows, when dealing with Web Assembly, or in embedded contexts) and sentiment suggesting that small and/or simple problems just don't really need a debugger. It does leave one wondering if the user experience could be made convenient enough to dethrone print debugging, but it seems hard to beat something so intuitive. This is followed by roughly 37% of respondents who write code that Just Works. Fair enough. After that, about 26% of respondents indicated that they've decided not to use debuggers in situations where the language features they were working with had poor support. This is slightly more than issues with standard library types, at about 22%, which is slightly more than issues with external library types, at about 20%:

<noscript> <img alt="when-you-dont-use-a-debugger-why-dont-you" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/when-you-dont-use-a-debugger-why-dont-you.png" /> </noscript>

As stepping through code was anticipated to be one of the most common uses for debuggers, we directly asked respondents if they faced any issues when doing so. A little over 51% of respondents said they did! Of those who reported that they experienced issues stepping through code, we asked when they were experiencing issues. Async code was the most common case reported at slightly over 28%, followed by code involving macros at about 23%. The least common case reported was code involving function pointers, at almost 6%:

<noscript> <img alt="when-do-you-experience-issues-with-trying-to-step-through-code-with-your-debugger" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/when-do-you-experience-issues-with-trying-to-step-through-code-with-your-debugger.png" /> </noscript>

We also directly asked respondents which types in the standard library were hard to work with, if any. This was an open-response question, and reading through the responses, some particularly common complaints were with enums and collections, particularly std::collections::HashMap and std::vec::Vec. This is also visible in the word cloud in the full report.

We asked respondents to indicate which pain points, if any, they have encountered when using debuggers with Rust. At slightly over 74%, poor representation of values was the most common pain point by a decent margin, followed by being unable to print variables at just over 55%:

<noscript> <img alt="which-of-these-pain-points-have-you-experienced-using-a-debugger-with-rust" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/which-of-these-pain-points-have-you-experienced-using-a-debugger-with-rust.png" /> </noscript>
[PNG] [SVG]

Debugger Visualizers

We asked respondents to indicate if they were library authors, and if so, if they were aware of and using the debugger_visualizer attribute. Nearly 62% of respondents indicated that they were library authors who were not aware of this attribute:

<noscript> <img alt="if-you-are-a-library-author-are-you-aware-of-and-using-the-debugger-visualizer-attribute" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/if-you-are-a-library-author-are-you-aware-of-and-using-the-debugger-visualizer-attribute.png" /> </noscript>
[PNG] [SVG]

For those who indicated that they were library authors who knew about the attribute but did not use it, we also asked why. This represented a much smaller fraction of respondents, so keep that in mind! That said, half of these library authors indicated that they didn't have the time to maintain visualizer attributes, and just under half indicated they didn't know how to write visualizer scripts:

<noscript> <img alt="why-dont-you-use-the-debugger-visualizer-attribute" src="https://blog.rust-lang.org/2026/09/07/rust-debugging-survey-2026-results/why-dont-you-use-the-debugger-visualizer-attribute.png" /> </noscript>

For those of you who have been reading this section asking yourself what the debugger_visualizer attribute is, you can read up on it in The Rust Reference: Debugger Attributes. The quick explanation is that the debugger_visualizer attribute can be applied to modules or the crate root to embed files in the debug information which improve the display of values with certain debuggers. The two currently supported file types are Natvis files, used by Microsoft debuggers such as WinDbg, and GDB "pretty printers", which are structured Python scripts used by GDB.

Closing Remarks

Thanks to your participation in this survey, we've gained some great insights about how Rustaceans are using debuggers and what issues they are facing. For example, knowing that such a high number of users are dealing with poor representation of values pairs well with knowing which standard library types are causing issues, knowing that many library authors haven't heard of the debugger_visualizer attribute, and knowing that many of those who have but don't use it either don't know how or don't have time to maintain visualizer scripts.

Looking to the future, the survey results have suggested that there are a few notable ways we could most significantly improve the debugging experience in Rust, such as:

  • Fixing the way enums are represented by debuggers so they show actual variants
  • Fixing the way collections (such as HashMap) are represented by debuggers so they show their contents, rather than their implementation details
  • Fixing the way string types (such as String and CString) are represented by debuggers so they render as text, rather than their implementation details
  • Improving the async debugging experience, particularly with stack traces
  • Improving stepping through certain state machines (such as iterators and Futures)
  • Providing documentation on basic set up and use of some common debuggers

A common suggestion that could resolve those first three points is to use the Debug implementation of types to display them in debuggers. There are challenges to that approach, such as the fact that the Debug implementation is not present in the final binary unless it is actually used somewhere in the program, but it isn't impossible. Notably, this is already supported by the BugStalker debugger (given the same condition that the Debug implementation must actually be used), which some of you first heard about from the survey! It also appears to have some support for async, with plans to expand.

One notable way the debugger experience is currently being improved is through the ongoing Google Summer of Code project improving how we test debug info and visualizer scripts, making it easier to maintain and improve our own visualizer scripts and general compatibility with visualizer scripts without silent breakage or regressions.

Once again, we'd like to thank everyone who took the time to participate in the survey!

Firefox Tooling Announcements

Firefox DevTools MCP 0.10.2 released

Firefox DevTools MCP (firefox-devtools-mcp) 0.10.2 is out on npm.

New tools:

  • press_key — sends a single key, optionally with modifiers, to a snapshot element or the focused element (Return, Escape, Tab, arrows, ctrl+shift+t, …).
  • type_text — types text key by key into the focused element, with an optional key to press afterwards.
  • set_network_cache — bypass or restore the HTTP cache, for the selected tab or browser-wide. Useful before a performance measurement or when verifying a change a cached asset would otherwise hide.

Other changes:

  • navigate_page and new_page take an optional wait argument (none, interactive, complete), so an agent can wait for the load event before deciding a page is done.
  • Windows: per-user Firefox installs are now detected, instead of failing with “unable to find binary in default location”.
  • aarch64 Linux: geckodriver is now resolved on all platforms, fixing “Unable to obtain browser driver” when a native geckodriver is on PATH.
  • Several Windows path fixes for --output-file, --log-file and saveTo.
  • New generated reference page documenting every tool and its parameters: docs/tools.md (firefox-devtools-mcp/docs/tools.md at main · mozilla/firefox-devtools-mcp · GitHub)

Install:

claude mcp add firefox-devtools npx @mozilla/firefox-devtools-mcp@latest

codex mcp add firefox-devtools – npx @mozilla/firefox-devtools-mcp@latest

For internal Firefox development, swap firefox-devtools-mcp with firefox-devtools-mcp-moz in order to benefit from additional tools, such as chrome-privileged script execution.

Special thanks to all the contributors who filed issues and submitted patches for this release: freema, f3tch (github), jasonanovak and shoemoney.

Full changelog: https://github.com/mozilla/firefox-devtools-mcp/releases/tag/v0.10.2
Repository and issues: GitHub - mozilla/firefox-devtools-mcp: Model Context Protocol server for Firefox DevTools - enables AI assistants to inspect and control Firefox browser through WebDriver BiDi · GitHub
Public chatroom: https://chat.mozilla.org/#/room/#firefox-devtools-mcp:mozilla.org

1 post - 1 participant

Read full topic

Firefox Tooling Announcements

PerfCompare just deployed (Sept 3, 2026)

Firefox Tooling Announcements

Firefox Profiler Deployment (September 3, 2026)

The latest version of the Firefox Profiler is now live! Check out the full changelog below to see what’s changed:

Highlights:

  • [Andrew Creskey] Show which network requests were prefetched (#6259)
  • [Florian Quèze] profiler-cli: accept --limit 0 as unlimited, and make truncation loud (#6267)

Other Changes:

  • [Nazım Can Altınova] Update oxfmt 0.59.0 → 0.63.0 (major) (#6262)
  • [Florian Quèze] profiler-cli: document the marker field:value search syntax (#6265)
  • [Florian Quèze] profiler-cli: report one time base for text and JSON output (#6266)
  • [Florian Quèze] profiler-cli: avoid a stack overflow on large marker threads (#6264)
  • [Markus Stange] Give the frameTable a lib column (#6258)
  • [Florian Quèze] profiler-cli: report the network request count the filters ran against (#6274)
  • [Nazım Can Altınova] Fix dark mode contrast of the warning icons in the publish panel (#6280)
  • [Nazım Can Altınova] Extract the publish panel warning indicator into a small component (#6282)
  • [Markus Stange] More typed arrays in the FrameTable (and a flags column) (#6173)
  • [Nazım Can Altınova] Improve the profiler-cli publish script and document the whole deployment in a better way (#6260)
  • [fatadel] :clockwise_vertical_arrows: Sync: l10n → main (September 3, 2026) (#6301)
  • [fatadel] Bump profiler-cli version to 0.9.0 (#6302)

Big thanks to our amazing localizers for making this release possible:

  • es-CL: ravmn
  • nl: Mark Heijl
  • sv-SE: Andreas Pettersson
  • sv-SE: Luna Jernberg
  • sv-SE: Peter Kihlstedt
  • tr: Selim Şumlu
  • tr: giray
  • zh-CN: 高乐喆

Find out more about the Firefox Profiler on profiler.firefox.com! If you have any questions, join the discussion on our Matrix channel!

1 post - 1 participant

Read full topic

The Rust Programming Language Blog

Announcing Rust 1.98.1

The Rust team has published a new point release of Rust, 1.98.1. Rust is a programming language that is empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, getting Rust 1.98.1 is as easy as:

rustup update stable

If you don't have it already, you can get rustup from the appropriate page on our website.

What's in 1.98.1

Rust 1.98.1 fixes a miscompilation in vtable generation.

In Rust 1.98.0, in some circumstances, rustc would incorrectly generate a trait object vtable with a null pointer where a function pointer should be. This leads to undefined behavior in the emitted code. In some cases this may 'just' cause segfaults due to the null pointer being loaded, but it is possible for it to be justification for arbitrary effects (as is typical for UB).

If you'd like to help us out by testing future releases, you might consider using the beta (rustup default beta) and nightly (rustup default nightly) channels locally and in your CI. Please report any bugs you might come across!

Contributors to 1.98.1

Many people came together to create Rust 1.98.1. We couldn't have done it without all of you. Thanks!

The Rust Programming Language Blog

Announcing rustup 1.29.1

The rustup team is happy to announce the release of rustup version 1.29.1.

Rustup is the recommended tool to install Rust, a programming language that empowers everyone to build reliable and efficient software.

What's new in rustup 1.29.1

The headlines of this release are:

  • Concurrency in certain rustup operations has been improved:

    • When running rustup update, rustup will first check for possible updates in parallel. pr#4752
    • When running rustup component add with multiple components, they will be installed concurrently. pr#4790
  • Implicit installation of the active toolchain in rustup-init and rustup invocations has been deprecated where deemed unnecessary and will now produce a warning. pr#4840

    • Please see our blog post for more details regarding this change.
  • rustup doc now supports the --serve flag which allows serving the docs over local HTTP. This should help users with containerized browser and/or rustup setups. pr#4986

  • Installing i686-pc-windows-* host toolchains on 64-bit Windows now requires --force-non-host. pr#4935

  • rustup-init will no longer leave unexpected files on disk after cancelled installations. pr#4996

  • A bug has been fixed which might cause Windows installation to fail when using rustup-init.sh. pr#4756

  • "Target triple" has been renamed to "target tuple" across the project to reflect the new terminology. pr#4743 pr#4827 pr#4834

    • Please note that this is not a breaking change in the CLI since the existing options such as --target are not using this terminology.

In addition, rustup now officially supports aarch64-pc-windows-gnullvm as a host platform. pr#4523

Further details are available in the changelog!

How to update

If you have a previous version of rustup installed, getting the new one is as easy as stopping any programs which may be using rustup (e.g. closing your IDE) and running:

$ rustup self update

Rustup will also automatically update itself at the end of a normal toolchain update:

$ rustup update

If you don't have it already, you can get rustup from the appropriate page on our website.

Rustup's documentation is also available in the rustup book.

Caveats

Rustup releases can come with problems not caused by rustup itself but just due to having a new release.

In particular, anti-malware scanners might block rustup or stop it from creating or copying files, especially when installing rust-docs which contains many small files.

Issues like this should be automatically resolved in a few weeks when the anti-malware scanners are updated to be aware of the new rustup release.

Thanks

Thanks again to all the contributors who made this rustup release possible!

The Servo Blog

July in Servo: more platforms, faster canvas, web fonts in SVG, and more!

Servo 0.5.0 contains all of the changes we landed in July, which came out to 488 commits, and we now publish binaries for Linux aarch64 (@mukilan, #46760)!

DOM text selections are now visible (@mrobinson, @SimonSapin, #46698, #46864, #46742, #46889, #46126). Interactive selection is coming soon!

For security fixes, see § Security.

We’ve shipped several new web platform features:

Plus a bunch of new DOM APIs:

This is another big update, so here’s an outline:

You can help!

If you’re working on a pull request that you think might be interesting for the next monthly update, even if you’re not 100% sure, tell us about it by following the steps below:

  1. You add the monthly update label to your pull request, or comment @servo-highfive monthly update

  2. Highfive posts a comment asking you some questions

  3. You answer those questions in a comment containing @servo-highfive monthly update answer

Security

Servo was potentially affected by vulnerabilities in quick-xml and crossbeam-epoch that have been fixed in Servo 0.5.0 (@atouchet, @Loirooriol, #46737, #46324). For more details, see RUSTSEC-2026-0194, RUSTSEC-2026-0195, and RUSTSEC-2026-0204.

We’ve updated ANGLE from a version based on Firefox 115.x ESR (02755361e26d8) to a version based on Firefox 140.12.0 ESR (f8025617e815f), which likely includes many security fixes (@jschwe, @sagudev, #46455, mozangle#100).

Real world compat

The duck on the DuckDuckGo (duckduckgo.com) landing page now renders in v0.5.0, after we fixed a preload bug that affected SVG images (@jdm, #46668).

v0.4.0
v0.5.0
duckduckgo.com

Most of Gumroad (gumroad.com), except for the landing page, did not render at all in v0.4.0, but as of v0.5.0, pages like the Discover page or this product page render almost perfectly.

v0.4.0
v0.5.0
gumroad.com
v0.4.0
v0.5.0
gumroad.com

We’re interested to hear how well your favourite websites run in Servo! Report successes in this Zulip thread, and failures in our GitHub issues.

Work in progress

The upgrade to Stylo 2026-07-01 brings several changes to built-in CSS functions (@Loirooriol, #46129):

  • ‘alpha()’ is now supported, under --pref layout­_css­_alpha­_color­_function­_enabled

  • ‘progress()’ is now supported, under --pref layout­_css­_progress­_function­_enabled

  • ‘ellipse()’ values ‘closest-corner’ and ‘farthest-corner’ are no longer stable due to spec uncertainty, but they are still experimental, under --pref layout­_css­_ellipse­_corners­_enabled

  • ‘attr()’ is more conformant, under --pref layout­_css­_attr­_enabled

Web­GPU content can now enjoy better conformance and use GPU­External­Texture and import­External­Texture() on GPU­Device, under --pref dom­_webgpu­_enabled (@sagudev, #45873, #46178, #46286).

Indexed­DB content can now use the name property on IDB­Index, under --pref dom­_indexeddb­_enabled (@skyz1, #45512).

document.fonts now includes a Font­Face for each valid ‘@font-face’, under --pref dom­_fontface­_enabled (@simonwuelker, #46509, #46537).

All of the features above are enabled in servoshell’s experimental mode.

We’ve started implementing WebVTT for native subtitles and captions, enabled by default (no --pref). While they don’t render just yet, we can now fetch each <track src>, parse the WebVTT, and expose cues via the track property on HTML­Track­Element (@TimvdLippe, #46289, #46383).

July was a big month for accessibility in Servo, under --pref accessibility­_enabled. The focus for this month has been on performance, with the accessibility tree now supporting incremental updates (@alice, @delan, #45578, #45971, #46589, #46691, #46385), requiring fewer HashMap lookups and tree walks (@alice, @delan, #45798, #46740, #46348), and allowing for faster DOM mutations (@alice, #46348, #46530).

We’ve also started working on the File and Directory Entries API, to allow users to select and upload entire directories via <input type=file> and drag-and-drop. To that end, we now have webkit­Get­As­Entry() on Data­Transfer­Item, plus minimal support for File­System­Entry, File­System­Directory­Entry, and File­System­File­Entry, under --pref dom­_entries­_api­_enabled (@yezhizhen, #46456, #46879, #46832).

Embedding API

We’ve improved the docs for the servo crate, and for Web­View­Delegate (@mukilan, #46193).

Breaking change: Servo­Builder::webxr­_registry() has been removed. Instead use the new Servo::register­_webxr­_registry, which is a lazy design that has allowed servoshell to halve its startup time (@Narfinger, #46494).

For users and developers

servoshell for Android now runs on Android 10+ (91% market share), not just Android 13+ (68% market share), improving adaptability and reducing waste (@jschwe, #46142, #46308). We’ve also fixed a problem with building for Android on macOS (@jschwe, #46128).

servoshell for Windows is now better behaved when run in a console window, making the command prompt wait until servoshell exits (@yezhizhen, #43010).

When using the Firefox DevTools, the Console tab now supports some basic autocomplete (@freyacodes, #46382).

We’ve finished modernising servoshell for Android to use Compose UI (@veyndan, #46085, #46164, #46253, #46257, #46317, #46353, #46565, #46612, #46626, #46666, #46663, #46700), and we’re now migrating Servo as a library to use Kotlin (@veyndan, #46817, #46895, #46772).

More on the web platform

Inline SVG can now use web fonts defined in the containing page (@yodalee, #45979). We’re also implementing the SVG DOM, starting with stub interfaces for SVG­Element, SVG­Circle­Element, SVG­Defs­Element, SVG­Ellipse­Element, SVG­Line­Element, SVG­Linear­Gradient­Element, SVG­Path­Element, SVG­Polygon­Element, SVG­Polyline­Element, SVG­Radial­Gradient­Element, SVG­Stop­Element, SVG­Rect­Element, SVG­Symbol­Element, and SVG­Use­Element (@mu-mostafa98, #46558).

<button> now vertically centers its contents (@Loirooriol, @mrobinson, #46590), and behaves better with ‘display: block’ and ‘display: inline’ (@Loirooriol, #46536).

We’ve improved the conformance of <form> without <form action> (@kevlu93, #46860), <color> values (@Loirooriol, #46129), Gamepad­Event (@log101, #46788), document.execCommand(“delete”) (@Psychpsyo, #46539), the selector­Text property on CSS­Style­Rule (@simonwuelker, #46687), and Set Window Rect in WebDriver (@janeoa, #46475, #46477).

We’ve fixed bugs related to <iframe> (@jschwe, @jdm, #46587), <img> (@yodalee, #46892), <textarea> (@SimonSapin, @mrobinson, #46309), custom properties (@Loirooriol, #46129), ‘::before’ and ‘::after’ (@Loirooriol, #46640), ‘flex-direction: column’ (@simonwuelker, #46697), ‘float’ (@Loirooriol, @mrobinson, #46407, #46500, #46505), ‘@font-face’ (@simonwuelker, #46568, #46271, #46436), ‘position: absolute’ (@simonwuelker, #46358, #46637), Blob (@jdm, #46881), IDB­Database and IDB­Object­Store and IDB­Index (@mrobinson, #46615), the adopted­Style­Sheets property on Shadow­Root (@simonwuelker, #46738), delete() on Font­Face­Set (@simonwuelker, #46634), move­Before() on Element (@mrobinson, #46599), resize­To() on Window (@janeoa, #46477), the selected property on HTML­Option­Element (@rhit-kapilaar, #46386), and the value property on HTML­Select­Element (@simonwuelker, #46230).

Performance and stability

2D canvas rendering is now multithreaded, improving frame rates by up to 55% and power consumption per frame by up to 42% (@yezhizhen, #46410), and should use a lot less memory too (@jschwe, @sagudev, #46786).

Text rendering is up to 10x faster for cases with the same text and different ‘font-size’ (@Loirooriol, #46129).

Flex layout benchmarks are up to 3% faster, and an improvement to get­Elements­By­Class­Name() has made some websites up to 1% faster (@Narfinger, @jdm, #46563, #46595, #46594).

We’ve also reduced memory usage, allocations, GC rooting steps, and other operations in many parts of Servo (@mrobinson, @jdm, @yezhizhen, @Narfinger, @Gae24, @SimonSapin, @Taym95, @cychronex-labs, @arayaryoma, #46499, #46411, #46659, #45974, #46377, #45758, #46440, #46762, #46301, #46349, #46419, #46418, #46420, #46460, #46633, #46638, #46690, #46745, #46726, #46564, #46144, #46664, #46462, #46139, #46430, #46446, #46498, #46548, #46598, #46632, #46656, #46678, #46718, #46722, #46238, #46072, #46408, #46438, #46437, #46528, #46124, #46330, #46412, #46807).

We’ve fixed a crash regression with memory corruption (@mrobinson, #46316), several dynamic-borrow-related crashes (@Narfinger, @SharanRP, @Taym95, @agrawalx, @amittenak47, @sungmen, #46381, #46384, #46405, #46684, #46452, #46770, #46830, #46763), plus crashes related to:

We’ve continued our long-running effort to use the Rust type system to make Servo’s integration with SpiderMonkey safer and more reliable (@Gae24, @Narfinger, @TimvdLippe, @jdm, @kunalmohan, @lumiscosity, @simonwuelker, #46191, #46777, #46890, #46243, #46248, #46246, #46310, #46312, #46333, #46147, #46150, #46151, #46229, #46262, #46375, #46374, #46529, #46584, #46585, #46593, #46693, #46166, #46156, #46254, #46267, #46268, #46269, #46270, #46284, #46285, #46318, #46435, #46461).

New contributors

A special thanks to the following people for landing their first patch in Servo:

Interested in helping build a web browser? Take a look at our curated list of issues that are good for new contributors!

Donations

Thanks again for your generous support! We are now receiving 7824 USD/month (+1.8% from June) in recurring donations. This helps us cover the cost of our speedy CI and benchmarking servers, one of our latest Outreachy interns, and funding maintainer work that helps more people contribute to Servo.

Servo is also on thanks.dev, and already 35 GitHub users (same as June) that depend on Servo are sponsoring us there. If you use Servo libraries like url, html5ever, selectors, or cssparser, signing up for thanks.dev could be a good way for you (or your employer) to give back to the community.

We now have sponsorship tiers that allow you or your organisation to donate to the Servo project with public acknowlegement of your support. If you’re interested in this kind of sponsorship, please contact us at join@servo.org.

7824 USD/month
10000

Use of donations is decided transparently via the Technical Steering Committee’s public funding request process, and active proposals are tracked in servo/project#187. For more details, head to our Sponsorship page.

Firefox Tooling Announcements

MozPhab 2.18.0 Released

Bugs resolved in Moz-Phab 2.18.0:

  • bug 1898339 moz-phab incorrectly uploads symlinks as text files when using Mercurial
  • bug 2063674 Consider reminding people of their review queue when they push to try or moz-phab
  • bug 2064223 Add pyrefly type checking to moz-phab test suite

Discuss these changes in #engineering-workflow on Slack or #Conduit Matrix.

1 post - 1 participant

Read full topic

Serge Guelton

Pros and Cons of Unified Build

Unified builds (also know as Jumbo Builds) is a build techniques that aims at improving build time through the concatenation of several sources as a single unified source before compilation.

The goal is obtained through implicit caching of header instantiation, although it implies a trade-off with parallelism.

Let's illustrate this behavior through a simple example, two codes that implement variation of the same approach:

/* algo0.cpp */
#include<iostream>
#include<string>
#include<vector>
voidtranslate(std::vector<std::string>&w,void(&t)(std::string&));
voidtranslate(std::vector<std::string>&w_out,std::vector<std::string>const&w_in,void(&t)(std::string&)){
std::cout<<"[log] through transform\n";
w_out=w_in;
translate(w_out,t);
}

/* algo1.cpp */
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
voidtranslate(std::vector<std::string>&w,void(&t)(std::string&)){
std::cout<<"[log] through for_each\n";
std::for_each(w.begin(),w.end(),[&t](std::string&s){t(s);});
}

Compiling individual files take the following times:

%hyperfine--warmup5"/usr/bin/clang++ -O2 algo0.cpp -c"
Benchmark1:/usr/bin/clang++-O2algo0.cpp-c
Time(mean±σ):267.7ms±7.4ms[User:234.6ms,System:30.4ms]
Range(min…max):259.7ms…277.3ms11runs

%hyperfine--warmup5"/usr/bin/clang++ -O2 algo1.cpp -c"
Benchmark1:/usr/bin/clang++-O2algo1.cpp-c
Time(mean±σ):173.6ms±46.3ms[User:149.6ms,System:22.1ms]
Range(min…max):130.4ms…231.6ms13runs

Creation of the unified file is just a matter of invoking cat, let's benchmark the compilation of the unified source:

%catalgo{0,1}.cpp>unified_algo.cpp
%hyperfine--warmup5"/usr/bin/clang++ -O2 unified_algo.cpp -c"
Benchmark1:/usr/bin/clang++-O2unified_algo.cpp-c
Time(mean±σ):223.8ms±64.5ms[User:193.0ms,System:28.4ms]
Range(min…max):160.8ms…301.8ms10runs

In that simple case, the weight of headers with respect to actual user code is such that compilation of the unified file takes almost the same time as the max compilation time among each individual file. That's roughly a 1.97x speedup on compilation time.

That's the promise given by unified builds. And it's a promise held.

Now let's have a look at the consequences of that deal.

Beforehand, we still need to introduce another parameter tied to unified builds: the unification parameter, say P. That parameter bounds the number of files that are unified together. Let's imagine we have a hundred of individual source files compiled with exactly the same compilation flags. Setting P to 5 leads to the generation of 20 unified sources compiled independently.

Remember the parameter P.

Quality of the Generated Code

Let's create a shared object from algo{0,1}.o (this implies a recompilation with -fPIC of the sources):

%/usr/bin/clang++-O2algo0.cpp-c-fPIC
%/usr/bin/clang++-O2algo1.cpp-c-fPIC
%/usr/bin/clang++-sharedalgo{0,1}.o-fPIC-oalgo.so

And do the same from unified_algo.o:

%/usr/bin/clang++-O2unified_algo.cpp-c-fPIC
%/usr/bin/clang++-sharedunified_algo.o-fPIC-ounified_algo.so

After stripping, comparing the size of the binaries yield a difference of a few bytes. After disassembling, it turns out the compiler decides to inline the call to void translate(std::vector<std::string>& w, void (&t)(std::string&)) from algo0.cpp when compiling the unified source, something the compiler cannot do when doing split compilation, as it does not know anything about the implementation of that function.

Interestingly, compiling with -flto=thin still lead the compiler instantiation through different optimization path.

Falling back to -flto=full finally yields to the same shared object, which makes sense because Full LTO is very close to performing source unification at the bytecode level and our sources are very simple. It's not a given though because the actual optimisation pipeline is still different in the two scenario.

Why does it matter? Depending on the value of P, the compiler will see different sets of files per unified file, which will result in different binary code. It's actually even worse: depending on the way we fill those unification sets, event with the same parameter P, we end up with different binaries. Let's call that the reunifying problem.

Even if we have an algorithm that seems to guarantee reproducibility, for instance working on a sorted list of files with a fixed P, variation can arise: the introduction of a new source file can lead to changes in every unified file (e.g. if the split is done by chunks and the new file ends up at the beginning of the file list).

So unified builds tend to improve performance, but they do not interact in a gentle way with performance reproducibility.

Recompilation Times

Let's denote S as the number of sources and C as the number of CPUs.

Intuitively, setting P=1 yields to the faster recompilation time when a single file is touched---a usual scenario when developing a new feature.

On the opposite, setting P=S yields to the slower recompilation time (if S >> C!) under the same scenario as all sources are recompiled under that scenario.

The form of the curve between those two extreme varies depending on the nature of the files, and the amount of header sharing between individual sources.

Caching tools like sccache is impacted by the same mechanism: as P gets greater, more cache misses are hit and more recompilation are done.

Marginally, introducing a new source also pollutes the cache or triggers recompilation for the unified source it gets added to, and eventually for all the unified sources derived from the associated file list. The reunifying problem strikes again.

So unified build make compilation faster, but recompilation slower. Setting P to an acceptable value is important depending on the usage scenario.

Correctness

Unified build changing the compilation unit frontier, which in turns modifies the semantic of the program. This change can be straight-forward or complex to debug, and even remain silent. I've listed a few instances of the two first categories below, and a crafted one for the latter category.

Macro / Symbol Redefinition

This one is trivial to spot (a preprocessor-warning is issued for the macro, and a compiler error is issued for the symbol redefinition):

/* pi0.cpp */
#define PI 3.141593
constexprdoublepi(){return3.141593;}

/* pi1.cpp */
#define PI 3.14159265
constexprdoublepi(){return3.14159265;}

The solution usually lies in moving the definition in a shared header, moving the declaration in a shared header and the definition in a single file, or renaming identifiers to avoid the name conflict. Note that depending on the solution we may change the visibility of the symbols, or impact code readability (assuming the identifier name was perfectly chosen in the first place).

Overload Conflicts

This one is also trivial to spot and may hint toward debatable design. But it exists and may be more complex to understand than the above:

/* overload0.cpp */
staticfloatdoit(floatf){returnf;}
constfloatf=doit(1);

/* overload1.cpp */
staticdoubledoit(doubled){returnd;}
constdoubled=doit(1);

The fix is generally to provide a perfect match for the overload, change the call site to avoid the ambiguity, or rename the functions/change their namespace to make the call site explicit.

Using Namespace Confusion

This one tends to creep a lot in codebase where using namespace is used. It generates ambiguity among potential symbols.

A caricatured situation is exhibited with the following situation:

/* using.h */
#pragma once
namespacea{
namespacea{}
}

/* using0.cpp */
#include"using.h"
usingnamespacea;

/* using1.cpp */
#include"using.h"
usingnamespacea;

Once using{0,1}.cpp unified, the second using namespace a; directive is ambiguous.

A more realistic (but similar in spirit) situation arises when the same symbol is defined in different namespaces:

/* namespace0.cpp */
namespacea0{
intvar;
}
usingnamespacea0;
intfoo=var;

/* namespace1.cpp */
namespacea1{
intvar;
}
usingnamespacea1;
intbar=var;

The problem with that category is that the fix is quite unsatisfying: there is no way to limit the scope of a using directive, removing using directive can lead to very verbose codebase, renaming symbols to avoid conflicts goes against the very purpose of namespaces...

Delicatessen

I spent a lot of time nailing that one down, so I wrote a small reproducer to illustrate the problem.

%tail-n+1*.h*.cpp
==>header0.h<==
#ifndef H0
#define H0
namespacemozilla::dom{

classLockfinal{};

}
#endif

==>header1.h<==
#ifndef H1
#define H1

#include "header0.h"

classLock{};

classAutoUnlock{
Lock*lock_;
};
#endif

==>src0.cpp<==
#include "header1.h"

==>src1.cpp<==
#include "header0.h"

==>src2.cpp<==
namespacemozilla::dom{};
usingnamespacemozilla::dom;
usingnamespacemozilla;


==>src3.cpp<==
#include "header1.h"

Let me comment that layout a bit: We basically have two different classes named Lock: one lives in the mozilla::dom namespace, and one lives at top-level. In header1.h, although we include the definition of mozilla::dom::Lock, we also get the definition of ::Lock, so a straight reference to Lock is not ambiguous.

Concerning source files, src0.cpp, src1.cpp and src3.cpp just include headers while src2.cpp contains the infamous using namespace modilla::dom; statement.

Let's now consider various partition of the file list src0.cpp, src1.cpp, src2.cpp, src3.cpp:

%forpermin0,12,30,1,21,2,30,1,2,3;doprintf"unifying $perm... ";cat`evalechosrc{$perm}.cpp`|clang++-xc++--fsyntax-only2>/dev/null&&echook||echoko;done
unifying0,1...ok
unifying2,3...ko
unifying0,1,2...ok
unifying1,2,3...ko
unifying0,1,2,3...ok

Isn't that amazing? Some intermediate unification, namely 0,1;2,3 and 0;1,2,3 fail, but other unifications, namely 0,1,2,3 and 0;1,2,3 fail. Did you notice that both non-unified and full unified build succeeds, while some intermediate unification fail? What a disaster. This basically mean that given a set of sources, and without putting restriction on the language (like banning using statement), the only way to be sure that a unified build always succeeds whatever the chosen partition is to test every partition. Not very satisfying.

As a side effect, we can also deduce that adding a new source file to a set of files to be unified can break compilation in files that used to compile fine. That's another instance of the reunifying problem.

Changing Semantic

It is quite easy to derive from the above an example whose semantic change once unified. Let's slightly change the overload conflict example from above:

/* silent0.cpp */
#include<cstdio>
staticintdoit(intf){putchar('0');returnf;}
constintf=doit(1);

/* silent1.cpp */
#include<cstdio>
staticdoubledoit(doubled){putchar('1');returnd;}
constdoubled=doit(1);

When compiled independently, this results in a binary that prints a 0 and a 1 on the screen. But when compiled as a unified source, we only get a pair of 0.

Concluding Words

Remember that discussion between Luke and Yoda?

LUKE Vader. Is the dark side stronger?

YODA No… no… no. Quicker, easier, more seductive.

That's exactly my thoughts on unified builds: they give you quick wins in term of cold build speed and give faster builds. That's very good properties, and you rip the benefit of them very quickly. Then you realize that you're tied to a monster in terms of maintainability and developer experience, but you're already addict to the speed it gave you.

Firefox Tooling Announcements

Happy BMO Push Day! (20260824.1)

Github Link

The following changes have been pushed to bugzilla.mozilla.org:

  • Bug 1224099 - create 2fa user documentation
  • Bug 1832783 - Wrong error message for exceeding file size
  • Bug 2064475 - phab-bot leaves #release-managers review as non-blocking when a user manually adds it first
  • Bug 2043229 - Display a “Show External” button beneath attachments list to display all attachments that would redirect such as Github and Phabricator
  • Bug 2057679 - Update docker build process to pull in latest builds of vendered javascript libraries needed by the client
  • Bug 2061264 - Remove CSP header from REST API responses
  • Bug 2061831 - Add additional information to bugzilla comments in REST API if comment edited before
  • Bug 2061441 - Migrate Classification REST resource to native Mojo API
  • Bug 2064523 - [meta] tracking bug for BMO dependency upgrades
  • Bug 2053504 - Need to add index to tracking_flags_values table for better performance from /bzapi/bug and REST search
  • Bug 2055001 - Github Pull Requests: Look for etag value before fetching pull request data to cut down on rate limiting

Discuss these changes in the BMO Matrix Room

1 post - 1 participant

Read full topic

Jonathan Almeida

SSH access to Github over HTTPS

While using guest wifi during travels, I was trying to fetch some changes from a github remote and my git fetch is over SSH which happens over port 22. Most (TCP?) ports are blocked on public or hotel wifi, so with some help1 I learnt that Github serves SSH over 443 too.

A simple ssh config fixes this:

Host github.com
  HostName ssh.github.com
  Port 443
1

Claude.

William Lachance

Glean Dictionary + DuckDB

While working on Writing the Docs: 2026 Edition, I experimented a bit with updating the Glean Dictionary to incorporate some of the ideas in the essay, in particular those around reference documentation.

As a sort of demonstration that documentation about an application can be treated as a data product in and of itself, I hacked up a quick ETL pipeline to dump the JSON output of the Glean Dictionary into a DuckDB database that can be queried standalone.

Since the Glean Dictionary is just a static Netlify site, the DuckDB database can simply be published alongside as a standard file. This allows you to do fun things from the DuckDB console like:

memory D ATTACH 'https://glean-dictionary-DuckDB.netlify.app/data/glean_dictionary.DuckDB' AS glean_dictionary (READ_ONLY);
memory D SELECT type, count(*) AS metrics
         FROM glean_dictionary.metrics
         GROUP BY type
         ORDER BY metrics DESC;
┌─────────────────────────────┬─────────┐
│            type             │ metrics │
│           varchar           │  int64  │
├─────────────────────────────┼─────────┤
│ counter                     │   20057 │
│ event                       │    7256 │
│ string                      │    3545 │
│ labeled_counter             │    3275 │
│ timing_distribution         │    2481 │
│ custom_distribution         │    2169 │
│ quantity                    │    1406 │
│ boolean                     │    1078 │
│ text                        │     588 │
│ labeled_timing_distribution │     569 │
│ datetime                    │     404 │
│ memory_distribution         │     389 │
│ object                      │     369 │
│ labeled_custom_distribution │     340 │
│ rate                        │     215 │
│ dual_labeled_counter        │     210 │
│ string_list                 │     204 │
│ uuid                        │     181 │
│ timespan                    │     149 │
│ labeled_boolean             │     118 │
│ labeled_string              │      84 │
│ labeled_memory_distribution │      66 │
│ labeled_quantity            │      42 │
│ url                         │      26 │
└─────────────────────────────┴─────────┘
  24 rows                     2 columns

From there, you could either build an interface like the Glean Dictionary itself (much easier to do from a database than the somewhat harebrained dataclass soup I originally came up with) or any number of other data products or analyses. Here's an example marimo notebook that tracks the metrics added to each product over time:

Open in Molab

As you can see, the number of metrics did a big jump in 2024 (I imagine they finally moved Firefox Desktop over to Glean) with slower and steadier growth since.

Tom Ritter

webaudio fingerprinting on alibaba

There's a popular post going around about Alibaba doing WebAudio fingerprinting and in the process, mucking around with the author's Bluetooth headphones. Here's the tl;dr: browser fingerprinting is a far-too-pervasive method of tracking users across the web, but at least for WebAudio specifically, it's not very effective. Firefox has largely eliminated this fingerprinting vector.

I work on Firefox. Chrome, Brave, Safari probably have defenses against this. I'm pretty sure Chrome's WebAudio code was made approximately constant (like ours) many years ago. It probably still leaks CPU architecture though.

I don't fully agree with the adage "given enough eyeballs, all bugs are shallow" - I think a more true one might be "given enough victims, your attack code is going to change something that makes someone notice". And that's exactly what happened with... sorry, wrong link. That's exactly what happened with Alibaba's fingerprinting code.

If you want to test Alibaba's WebAudio fingerprint, I had claude extract it into a standalone page. My value is sha256:9a388c0dd04cfdc54314f9d961c7e2d247b972067e28d1cea76bd6060cf1392e, and the other value we saw in an internal survey was sha256:16d3191880ce01f726015ec6a1f9a072a81ebd04bf489098d4685d1d1c0b2711.

WebAudio Fingerprinting in Firefox

We made the WebAudio constant in Firefox 118 as part of our initial round of Fingerprinting Protection features. This eliminated most of the differences.

99.24% of users belong to one of three values, and .76% of users had that data collection point fail (a value of zero.) So virtually all users have one of these three values but why are there three values??? Why isn't there one?? Well this is why we collected this telemetry - to ensure we really had made things constant the way we thought we had, and if we hadn't, figure out why and fix it.

The improvements we put into 118 eliminated a lot of differences and I wish I had a graph I could show you of what it looked like before (but I don't.) But what about these remaining three buckets? We were able to figure out that the differences between these are CPU-level differences, which is not surprising given the math operations of audio processing. Specifically one value comes from (all) x86 CPUs and x64 CPUs that lack fused multiply-add instructions, one value from x64 with FMA, and one value from CPUs with the NEON instruction set (ARM).

We followed up on that effort to collapse these buckets into one another. Bug 2036977 collapses the x64 (with FMA3) into the x86/x64 (without FMA3) bucket. Bug 2040494 is on file to collapse the remaining bucket into the NEON bucket but I haven't worked on it in a while because it's just not as high a priority as larger improvements we can make like Sanitizing the WebGL Renderer and Vendor (something I talked a bit about a few months ago).

Because if you've read this far you surely care about the rest of the graph also. There is also the long-tail of 23 other values belonging to 48 other users. This is very unfortunate, as it makes these users completely unique, but it is also not terribly unusual - computers are weird and these results could have been caused by bad RAM, a CPU bug, or possibly some crazy architecture (LoongArch??).

But at the end of the day, WebAudio fingerprinting is nearly useless. I don't expect browser fingerprinting to disappear from websites entirely (unless some regulatory action occurs, fingers crossed) - it's still going to be effective against a majority of users on the web, but at least for privacy focused browsers, it should be wildly less effective.

Firefox Tooling Announcements

MozPhab 2.17.0 Released

Bugs resolved in Moz-Phab 2.17.0:

  • bug 2036748 submit: parallelise per-commit upload + creatediff across the stack
  • bug 2036749 git: skip restoring git checkout in cleanup/finalize when HEAD already on branch
  • bug 2060484 " Phabricator Error: Validation errors" should display the revision

Discuss these changes in #engineering-workflow on Slack or #Conduit Matrix.

1 post - 1 participant

Read full topic

Andreas Farre

Tracing algorithms through web specifications

A bug comes in, or someone shows me a page doing something surprising, and the question is always the same one. Is that what the spec says? Not what we all assume it says, and not what Gecko happens to do, but what the algorithm actually does when you follow it step by step. So I open html.spec.whatwg.org, find the entry point, and start reading. Twenty tabs later I have usually lost the thread.

This isn’t an occasional chore. I do it when I’m implementing something new, when I’m working out whether a Gecko bug is a bug in Gecko or a bug in the spec, when I’m reviewing a spec change someone else has proposed, and when I’m trying to design something that has to fit alongside everything that already exists. Four fairly different jobs, and every one of them comes down to the same mechanical work. Start at an algorithm, follow the calls, keep track of where you’ve been.

The specs are built for exactly this, in principle. Every step that invokes another algorithm is a hyperlink, and the whole thing is one large call graph spread across HTML, DOM, Fetch, URL and Infra. The trouble is that walking that graph by hand is slow, and it’s the kind of slow a machine should be doing on your behalf.

Two tools have changed how I do this. One answers questions about the graph, and the other helps me walk it. If you’d rather install them first and read afterwards, they’re both at the end.

webspec-index trace

webspec-index is Jan Jaeschke’s tool for querying web specifications from the command line. It indexes the specs, on demand as it needs them, and lets you ask about definitions, references and IDL. In August it gained a trace subcommand, and that’s the one I keep reaching for.

You give it two anchors and it finds the routes between them. It answers in JSON by default, which is the right choice for feeding it into something else but not for reading, so add --format markdown when the audience is you. Say I want to know how a navigation can end up firing navigateerror:

$webspec-index trace HTML#navigate HTML#event-navigateerror \
    --format markdown
# trace: `HTML#navigate` -> `HTML#event-navigateerror`

Max depth 6, kind `step`. Found 11 trace(s).

## Trace 1 (5 hop(s))

1) [`HTML#navigate` step 20](…#beginning-navigation:set-the-ongoing-navigation)
   calls `HTML#set-the-ongoing-navigation`
   > Set the ongoing navigation for navigable to navigationId.

2) [`HTML#set-the-ongoing-navigation` step 2](…#aborting-navigation:inform-the-…)
   calls `HTML#inform-the-navigation-api-about-aborting-navigation`
   > Inform the navigation API about aborting navigation given navigable.

3) [`HTML#inform-the-navigation-api-…` step 3.1](…#ongoing-navigation-tracking:…)
   calls `HTML#abort-the-ongoing-navigation`
   - under: While navigation's ongoing navigate event is not null:
   > Abort the ongoing navigation given navigation.

4) [`HTML#abort-the-ongoing-navigation` step 7](…#ongoing-navigation-tracking:…)
   calls `HTML#abort-a-navigateevent`
   > Abort event given error.

5) `HTML#abort-a-navigateevent` step 6 calls `HTML#event-navigateerror`
   > Fire an event named navigateerror at navigation using ErrorEvent, with
     additional attributes initialized according to errorInfo.

I’ve wrapped those lines and shortened the URLs to fit; in the real output each hop is one long line and every link is a full deep link to that exact call site.

Every hop names the exact step number, quotes the step text, and tells you the condition it sits under. That last part matters more than it looks. Step 3.1 only runs while the ongoing navigate event is non-null, and knowing that is the difference between a path that exists on paper and a path your case actually took.

Eleven routes, though1. Reading eleven of these back to back is not much better than the twenty tabs.

Three zoom levels

This is where webspec-index trace gets genuinely nice, and it’s the part I want to draw attention to, because it’s easy to miss in the help text. The --detail flag changes how much of each route you get, and the three levels turn out to be three different ways of working rather than just three verbosity settings.

Ask the same question again with --detail edges and the prose falls away, leaving the skeleton:

$webspec-index trace HTML#navigate HTML#event-navigateerror \
    --detail edges --format markdown
# trace: `HTML#navigate` -> `HTML#event-navigateerror`

Max depth 6, kind `step`. Found 11 trace(s).

## Trace 1 (5 hop(s))

1) `HTML#navigate` step 20 calls `HTML#set-the-ongoing-navigation`

2) `HTML#set-the-ongoing-navigation` step 2 calls `HTML#inform-the-navigation-api-about-aborting-navigation`

3) `HTML#inform-the-navigation-api-about-aborting-navigation` step 3.1 calls `HTML#abort-the-ongoing-navigation`

4) `HTML#abort-the-ongoing-navigation` step 7 calls `HTML#abort-a-navigateevent`

5) `HTML#abort-a-navigateevent` step 6 calls `HTML#event-navigateerror`

## Trace 2 (5 hop(s))

1) `HTML#navigate` step 22.4 calls `HTML#fire-a-push/replace/reload-navigate-event`

2) `HTML#fire-a-push/replace/reload-navigate-event` step 2 calls `HTML#inform-the-navigation-api-about-aborting-navigation`

...

Now you can read all eleven at once, and a shape appears that I could not see in the verbose output at all. There are five places inside navigate where a route starts:

step calls
15.1 #navigate-fragid
20 #set-the-ongoing-navigation
21.1 #navigate-to-a-javascript:-url
22.4 #fire-a-push/replace/reload-navigate-event
24.1 #checking-if-unloading-is-canceled

And all eleven finish on the same line:

`HTML#abort-a-navigateevent` step 6 calls `HTML#event-navigateerror`

There are exactly two ways into that step, from HTML#abort-the-ongoing-navigation step 7 and from HTML#process-navigate-event-handler-failure step 5. Eleven routes exist because three of the five entry points re-enter the same navigate event firing machinery from different directions, not because there are eleven genuinely different things going on.

That’s a useful thing to know before you start debugging. If a navigation is firing navigateerror and you want to find out why, you don’t instrument eleven places. You instrument the one step they all pass through and work backwards from there.

So the way I use it now is to start at --detail edges to find the shape, then drop back to the default once I know which route I care about and want to read the reasoning. The third level, --detail compact, drops the prose but keeps every anchor as a deep link to the exact call site, which is the form you want when you’re pasting a route into a bug report for someone else to follow.

WebSpec Tracer

webspec-index trace answers a specific kind of question, and it’s worth being precise about which kind. You have to know both ends. That’s fine when you’re asking whether one thing can reach another, but a lot of the time I only know where I’m standing. I have a starting point, a bug in front of me, and no idea yet where the algorithm goes.

There’s also something webspec-index trace can’t do for you in principle. It finds routes through the spec text, statically. It doesn’t know which branch your case took, because that depends on what actually happened at runtime, and only you have that information.

So I wrote WebSpec Tracer, a Firefox extension that walks the specs with you rather than ahead of you. You give it a starting anchor and it shows you that algorithm with every linked term highlighted. Click one and it becomes the next hop in the trace, and the panel moves you into it. Click the wrong one and Back undoes it.

The screenshot is a walk that started at location.assign and has arrived inside navigate. Step 15 is the interesting one. It runs only if four things are all true, that there’s no document resource, no response, the URL matches the current entry’s URL ignoring fragments, and the fragment is non-null. If all four hold, it navigates to a fragment and returns. Otherwise the algorithm keeps going into the full navigation path below.

No tool can decide that for me. Whether my bug went left or right at step 15 depends on the page and on what the user did. But the extension puts the decision in front of me with the alternatives visible, which is precisely what I was failing to do for myself with twenty tabs open.

The trace builds up in the panel underneath as markdown, with every anchor pointing at the exact call site. That box is a display rather than a text field, so there are buttons for getting the trace out of it. Copy does what you’d expect. Insert is the one I actually use, and it’s the reason the extension lives in the sidebar: it writes the markdown straight into whichever field you have focused on the page, so you can have a spec issue open next to the panel and drop the trace into it without going near the clipboard. Reset clears everything and starts over.

Switch the issue over to Preview and there are the same two hops, rendered, each one a link into the spec. That’s most of the reason I built the thing. Explaining a route through an algorithm in prose is miserable, and the version you retype by hand is always slightly wrong. This way what I hand over is exactly what I walked.

One more thing worth knowing, about the anchors themselves. The starting one has to say which spec it’s in, so HTML#dom-location-assign rather than just dom-location-assign, but after that the extension works it out as it goes. Anything that turns up in the trace without a prefix is in the spec you started from, and a prefix appears when the trace crosses a boundary:

That’s the same walk carried a few hops further. Hop 3 is that step 15 decision, taken, and the last line is WEBIDL#wait-for-all. Somewhere in the navigate event firing the trace left HTML entirely, and the prefix is the only thing that tells me so. The panel on the left has stopped showing me HTML too, and is now sitting in WebIDL. That’s easy to lose track of when you’re clicking through by hand, and it’s usually the moment a question stops being about HTML and starts being about something else.

When one call happens twelve times

Sometimes an algorithm invokes another one from many different places, and a trace has to say something sensible about that. This was in the design from the start, and the answer it arrived at is to do what the spec already does. Start at HTML#update-the-image-data and follow it to HTML#abort-the-image-request:

HTML#abort-the-image-request is invoked from twelve separate places inside HTML#update-the-image-data. The extension doesn’t make me choose one, and it doesn’t flatten them into a single link either. It records the hop once and hangs all twelve call sites off it as numbered links, so the line reads #update-the-image-data calls #abort-the-image-request, [2], [3] and onwards to [12], each number pointing at its own anchor.

The numbering isn’t invented. It’s the spec’s own scheme for repeated references: the anchors run #abort-the-image-request, then -2, -3, and so on up to -12. All the trace does is carry that through, so a hop in the output identifies call sites the same way the spec identifies them, and the numbers mean the same thing in both places.

That also keeps the trace honest. The hop happened, and that’s true whichever of the twelve fired, but the fact that there are twelve is worth knowing on its own, and anyone reading can go and check any one of them. Writing this out by hand you would link the first and lose the other eleven without ever noticing. The same thing is visible in the previous screenshot, where hop 3 reads #navigate calls #navigate-fragid, [2], [3].

webspec-index trace deals with the same situation the other way round. Ask it the same question and you get twelve separate one hop traces, each naming its own step number, step 2, then 7.4.2, then 11.1, and so on down the algorithm. Neither is wrong. The command line is telling you which steps, and the extension is keeping your trace to one line while preserving every place it could have come from.

Which one, when

They divide up cleanly enough that I don’t think about it much any more. If I know both ends and want to know what’s between them, or how many different routes there are, that’s webspec-index trace. If I know where I’m starting and want to find out where it goes, that’s the extension.

The one I underused at first was --detail edges. Asking a broad question and then zooming out far enough to see the answer’s shape is a different move from following a single path carefully, and it’s the one that has surprised me most often, usually by showing me that several things I thought were separate all funnel through the same step.

Getting them

webspec-index is on crates.io:

cargo binstall webspec-index

It builds its index as it goes, so the first trace into a corner you haven’t visited before pauses briefly to fetch and index the specs the route touches. After that it’s fast.

WebSpec Tracer is on addons.mozilla.org, and the source is on GitHub. It lives in the sidebar, toggled with Ctrl+Shift+U, so you can keep it open next to whatever you’re reading.

Both are worth a try if you spend any time at all reading specs, whether you’re implementing them, arguing with them, or just trying to find out whether the browser is wrong or you are.

Notes
  1. Eleven is what you get at the default maximum depth of six hops. Raise it and there are more. ↩

William Lachance

Writing the Docs: 2026 Edition

It's been about 5 years since I was working heavily on documentation and metadata at Mozilla. I wrote a bunch about this at the time:

I don't think I ever wrote about it explicitly, but in the background I (along with a few others at Mozilla Data) were digging deep into "proposal culture" and how to collaboratively build software together using design documentation.

Thinking hard about knowledge systems is a somewhat idiosyncratic obsession, and something I emphasize a little less day-to-day now. That said, I still think that putting effort into organizing information and making it legible both leads to better workplaces and makes us generally more productive. Fundamentally, it’s an act of care both to yourself and your peers, past and present.

Moreover, the core points I was trying to make at the time (either in what I wrote or what I did) mostly still hold:

  • Separate the "what" from the "how"
  • Reference documentation should derive from ground truth
  • Document your decisions and process
  • Continuous improvement

As a sort of retrospective, I'd like to revisit these principles 5 years on, with special attention to how they relate to large language models (LLMs).

I’m going to talk about Mozilla a lot because it’s all on the public record and I can talk about it relatively freely. I haven’t really been involved in the community or watching closely what’s been happening there since I left in January 2022. Likely the internal perspective at Mozilla these days is different from my own (hopefully not that different 😂).

Separate the what from the how

This is what I talked about in A principled reorganization of docs.telemetry.mozilla.org. While I wasn’t aware of it at the time, I was basically repeating the principles of the diataxis framework. Since then, diataxis has become my go-to for describing good documentation, because it's so well written and explained.[1]

diataxis breaks documentation down into four categories:

  • Tutorials: How to do a basic task, step by step: intended for beginners
  • Explanation: Longer documents describing the "why": intended for all
  • How-to: How to accomplish a specific task: intended for non-beginners
  • Reference: Ground truth establishing the "what": intended for all

I think this framework holds up really well, and it’s still the first thing I reach for when thinking about (or explaining) how to write something. Although diataxis was never fully applied, docs.telemetry.mozilla.org became a more useful site for data practitioners after I emphasized tutorials and "getting started" docs, and put the reference and deeper dives later on. It was pretty frequent that I'd be able to answer a question on our internal slack channel and be able to quote a step-by-step link from the docs describing exactly what someone needed to do or know.

My one quibble with diataxis is that sometimes for a complex task you need to mix the different types of documentation. Visualizing Percentiles of a Main Ping Exponential Histogram was written as a very long and sprawling mix of explanation with theory (even the name is a mouthful), but I think that’s one of its strengths? I got repeated feedback that it was extremely helpful by the Firefox engineers. I can't be totally certain it still holds up today, but I'm fairly certain it does. At the very least, no one has felt motivated to take it down.

Do LLMs change anything here? I would argue no. Although some of the things we want to do now are different (and still changing as of this writing), each has proven useful in its own way.

Tutorials and explanations are the easiest case to make: they are meant for general sense making, and the need for that hasn't gone away at all. Indeed I think as we delegate more of the day-to-day work to machines and reduce the amount of hands-on work which generates understanding, our need for these aids has only increased. I occasionally use LLMs to create initial drafts of this type of work and it’s interesting to see what they miss or get wrong: often some vital fact or nuance is not there, and they have a tendency to emphasize incidental details.

One thing that many people find surprising about tutorials is that they were actually never really meant to describe how to do something. Instead, the purpose is primarily just to give some hands-on experience and feeling of success, so they can start feeling more confident in going deeper and doing the more advanced tasks where explanation, how-to, and reference become more important. This need to gain a preliminary understanding of the system has not gone away at all with LLMs-- I maintain that hands-on learning and action is still the best way for most people to learn anything, including how to work with computational systems. Once you've gone beyond the basics, any gaps in understanding or context can be filled in by absorbing the content of deeper articles describing the concepts of a system and why it is the way it is.

Howto documentation (sometimes also called cookbooks or runbooks) are an interesting case, as I feel like this is where things have simultaneously changed the most and the least. Back in the day, I felt that most human written documentation should be exactly this type of thing: it was the most broadly useful and tended to age much better than explanation or reference material that would frequently go out of date. Being able to answer a slack question with a link to this type of material was always satisfying.

Of course now, often it's a large language model or similar executing these types of tasks. Early in 2026, the hot topic was creating Agent Skills which are, at heart, just little markdown docs named SKILL.md which describe how to perform a particular task. I think they are entirely complementary to the approach. You write a detailed howto describing how to perform a task (using Mozilla data as an example, query a table in BigQuery). An LLM can process a well-written markdown file just as easily as a human: just create an appropriate skill file saying as such:

---  
name: query-main-ping-telemetry-table*  
description: Query the main ping telemetry table in Bigquery*  
---

Refer to `docs/howtos/query-telemetry-table-in-bigquery.md`

I'm often surprised at how quickly modern large language models can figure out how to do something essentially from first principles, but if you've arrived at a good solution already, why bother doing that? Just give them a cookbook and save the time and the tokens. And if you need to debug what it's doing (or do part of it yourself), the exact set of steps is right there.

Reference documentation deserves its own treatment. More on it in the next section.

Reference documentation should derive from ground truth

In Mozilla Telemetry in 2020: From “Just Firefox” to a “Galaxy of Data” I talked about how Mozilla's data could no longer reasonably be explained with a few hand-written articles:

Up until now, our defacto answer has been some combination of “Use the probe dictionary / telemetry.mozilla.org” and/or “refer to docs.telemetry.mozilla.org”. I submit that we’re at the point where these approaches break down: as mentioned above, there are many more types of data we now need to care about than just “probes” (or “metrics”, in Glean-parlance). When we just cared about the main ping, we could write dataset documentation for its recommended access point (main_summary) and the raw number of derived datasets was manageable. But in this new world, where we have N applications times M ping types, the number of canonical ping tables are now so many that documenting them all on docs.telemetry.mozilla.org no longer makes sense.

I was already thinking about the Glean Dictionary and trying to justify working on it to my peers and upper management. In retrospect, this claim is self-evident: if you're dealing with hundreds or thousands of entities (or more), a manual approach to writing the documentation just isn't going to work. Even if you can generate a perfect first draft, you've only postponed the problem: the implementation will change, and independently maintained reference documentation will drift from it.

The solution is to make your reference documentation derive from the actual truth of the thing that you're trying to document. This means some kind of machine readable representation: the source code, metrics definitions, dataset definitions, or similar. Critically, the reference documentation should directly link back to that original source, so you can verify (and examine) where these ground truth assertions are coming from. This is why the Glean Dictionary included a link back to the original schema or artifact which generated the documentation:

https://dictionary.telemetry.mozilla.org/apps/firefox_desktop/metrics/a11y_always_underline_links

This is a pretty old idea, dating back to Sphinx’s autodoc and doxygen, although it hasn’t been super consistently applied in modern data engineering from 2020 to 2022 or so. These days, we tend to emphasize getting to an answer (“How many users did XYZ?”) over how the answer was derived. Mozilla was a bit of an exception in that it had (hopefully still has) a lot of curious engineers who really did care about these details.

When using an LLM, I feel like this type of ground truth is more critical than ever. Much has been written about the tendency of large language models to hallucinate. Conclusions drawn from inaccurate source data can compound, leading to yet more confusion and problems that only get caught at review time (if you're lucky). Having a clear statement of the system-as-it-currently-is (with citations) is the best defense against that. Not only does it reduce the chance of hallucination or confabulation, but it gives a human operator the chance to check the reference and see if it makes sense.

What was maybe a little more controversial (and possibly still is), was my conviction that writing a custom website was the right answer. The world is awash in SaaS companies offering to make sense of a data schema, service architecture, what have you-- why not just use those? At the time I wrote that article, we were considering Google's data catalog and I had to justify why creating a new thing wasn’t Not Invented Here:

A few months ago, I thought that Google’s Data Catalog (billed as offering “a unified view of all your datasets”) might provide a solution, but on further examination it only solves part of the problem: it provides only a view on your BigQuery tables and it isn’t designed to provide detailed information on the domain objects we care about (products, pings, measures, and tools). You can map some of the properties from these objects onto the tables (e.g. adding a probe’s description field to the column representing it in the BigQuery table), but Data Catalog's interface to surfacing and filtering through this information is rather slow and clumsy and requires detailed knowledge of how these higher level concepts relate to BigQuery primitives.

I haven't looked at Google's Data Catalog (or whatever it’s called now) since then, but I suspect that although it’s probably a great schema viewer, it's still going to be just as confusing for someone who doesn’t work as a data engineer day to day.

It's just inherent: unless your product is a database, the domain of a generic data catalog (schemas, tables) is going to be inherently different than that of your business. This is true whether what you care about is the experience of Firefox users or dispatching taxi cabs. Forcing a data user to think in terms of the former is to move them away from asking actual product questions.

The same really goes for any other "data catalog" SaaS product: in order to work across industries, it needs to express a bunch of concepts which are going to be foreign to your domain. If you're going to go to the trouble of mapping your own concepts and systems to the model of a product like this (which is honestly 90% of the effort, in my experience), putting in a little bit of extra effort to produce a custom interface or website might actually be the missing piece to make that work maximally impactful. This is even easier today thanks to large language models and generally evolving technology and practices.

One area where I do think there is still room for development and growth is infrastructure to create these custom tools and data representations. Partly based on my experiences with the Glean Dictionary (and associated tools like probe-scraper and mozilla-schema-generator), I've been working on a new framework called repo-parser which formalizes this concept of extracting this “ground truth”.

Given a git repository and a set of rules (e.g. “convert this set of markdown frontmatter into a set of tags”), it can take a forest of markdown, yaml, and json and produce either a duckdb database or an in-memory representation of the repository’s content. You can use this to populate a data catalog (like the Glean Dictionary), create a dedicated documentation site or any number of other things.

repo-parser’s example site is a mundane example of this. The content in the screenshot is produced from a README.md inside the repository, collected and organized dynamically at build-time:

I'm not certain if repo-parser is exactly the right abstraction for building something like this, or if it's more like an architectural design pattern where every implementation is bespoke. Time will tell.

Proposals, architectural decision records

Ground truth can tell us what, but doesn’t say much about why. When I introduced the afore-mentioned dictionary product in The Glean Dictionary, embedded within was a link to a Google docs proposal.

This was part of a larger movement towards "design docs" that myself and a few of my coworkers were pushing at the time. The idea was, before embarking on a large piece of work, you would write and circulate a large Google Doc containing a problem statement, goals, non-goals, and a preliminary expression of what you were trying to solve and alternatives considered. Then, discussion could proceed asynchronously over docs comments.

I didn't really write a lot about them because (I think) my coworkers wrote and presented on them extremely effectively and I was more interested in, well, the above. But I think they're just as valuable a kind of writing, they answer so many important product and engineering questions:

  • Why this design?
  • Why now? (or why then, if looking at the proposal retroactively)
  • How does this impact security, reliability, ... ?
  • What comes next?

I still think proposals are extremely valuable. The obvious benefit is they allow us to reject or modify sub-optimal designs before too much effort is spent building and/or supporting them. But just as importantly, they allow us to distribute understanding of what is being built and why across teams.[2] In software engineering, it’s one of the best ways I’ve found to align a diverse group of human beings around a single objective.

This wisdom was pretty old even then: the influential article on Design Docs at Google dates all the way back to 2015, best I can tell. There are other precursors -- for example, Painless Functional Specifications by Joel Spolsky way back from the year 2000. I'm sure you can find articles about this all the back to the 70s, or even earlier if you look hard enough. However, I've noticed that many organizations struggle to have a good proposal culture. If the ideas are so obvious, why?

Some issues go well beyond the scope of this article: I do think that having an effective proposal requires a strong commitment to psychological safety. If people don't feel like their design or feedback is going to be taken in good faith, the proposal process won't work very well. People will be hesitant to put things out there and to engage, and the opportunity for learning will be reduced. In a healthy workplace, people are constantly being guided towards presenting their best ideas, with the knowledge that their colleagues are there to support them.

Looking at how large language models have affected things, I have two answers: "almost not at all" and “totally".

For the traditional "big google doc" type proposals I talk about above, I lean towards thinking that these should continue to be used (more or less) the way they were before: as a means for human beings to align on the right solution to a problem. A large language model can help with the initial brainstorming producing the first draft, but to be maximally effective both the initial communication and response should be the product of human interaction. That ensures that the proposal is an expression of a fully fleshed out train of thought and represents real effort. Likewise, the responses to it represent a real attempt to engage with and learn from the content.

On the other hand, I think for our day-to-day where the implementation is largely produced by a large language model, I have found the above formula useful in an unexpected way: as a way to converse and convey requirements to a machine.

These days, I spend more of my time prompting an LLM than writing code myself. But since they have the memory of a goldfish, I’ve found getting them to produce the right thing a bit of a crapshoot. Yes, there is AGENTS.md but that can only tell it so much: there’s always more “implicit” context that is needed to solve a problem of any complexity. And even with extremely long context, important facts can be missed or given too little weight.

Since I found that I was encoding these details in a prompt, I thought: hey, why not just make the prompt a design doc? Have a human express the intent, goals, non-goals and a sketch of a solution in a markdown document and then collaborate with an LLM on the detailed design. Then, once you’re happy with the result, proceed with implementation. The result can then be checked in along with the code as a sort of guide to understanding what was done. In a pull request type work flow, it can be critiqued or commented on like any other code artifact.

If you’re familiar with plan mode, a lot of this might sound familiar. The key difference is a fairly deliberate structure (explicit dating and proposal template) and a higher level of human involvement in the earlier stages of design. To incorporate some notion of chronology (since IDRs are meant to be “moments in time”), I used a filename convention inspired by database migration tools like alembic and goose (for example: 202601180308-upload-packages-to-pypi.md) and a design template that should be familiar if you’ve read Design Docs at Google:

# 2026-01-18: Upload packages to PyPI

Owner: Will Lachance <wlach@protonmail.com>

## Overview

### Problem Statement

Save maintainer time and avoid security gotchas by using github actions to upload to pypi

### Context (as needed)

This repo ships a Rust CLI as a Python package via \`maturin\` (\`bindings \= "bin"\`). CI already builds wheels on Linux/macOS/Windows and runs a smoke test from the built wheel.

### Goals

-Make it easier to publish new releases  
-Improve security

### Non-Goals

-Automate version bumping (will still entail a seperate commit to bump version)

### Proposed Solution 

…

I've found this a nice additional specialization of proposals, and have found it really helpful in my own work. If you're curious about this, check out the idr-tools repository, where I've been exploring this idea further. You can see a few samples of the idea both there and in repo-parser (in the idrs subdirectory).

Always be improving

One through-line which is in the above work which I didn't blog about explicitly: always be improving. Typos, fixing bitrotted instructions, little improvements in clarity: over time this type of enhancement ensures that the documentation is maximally useful both to humans and machines.

https://github.com/mozilla/data-docs/commits?author=wlach

You can use an LLM to do this automatically, but obviously you want a human being to review the output. Either way, the end result is the same: better end-user documentation and an (infectious) demonstration of actual care. It’s a way of supporting others, including your future self. After some time, repeated small actions become a habit, others follow along and it fades into the background as “the way we do things around here”.

Conclusion

Although I wouldn't write the above articles in the same way (or possibly even write them at all) today, I do think the conclusions hold up-- in some cases even more strongly-- in the new, strange world of large language models. So long as there are complex systems, I don't think describing them in forms that are meant to explain will go out of style. If anything, it becomes more powerful and needed at all stages of the design and build process.

  1. Sometimes people talk about the divio documentation system, but as I understand it, it’s just a precursor to diataxis (created by the same author). ↩︎

  2. I believe the same is true of code review, but that’s another story. ↩︎

Jonathan Almeida

Regenerate the BuildConfig file without assembling the whole module

We have tokens that are added to official builds when a CI server is building the production-ready application. These tokens are added to the BuildConfig.java by using the buildConfigField function in a build.gradle.

While testing, you want to quickly regenerate just this file to see changes occur with a new value. Instead of using the more expensive assembleDebug task for your debug variant, you can use generateDebugBuildConfig.

In the Firefox monorepo world for the Fenix module, the fully-qualified task name would look like this:

./mach gradle :fenix:generateDebugBuildConfig

Niko Matsakis

Cylic trait implementations: motivation

Lately I’ve been thinking about cyclic trait implementations. This is a problem that I’ve been trying to understand for years and years and I finally feel like I’m geting somewhere. I’m going to try to write out a series of blog posts documenting those explorations and, hopefully, culminating in a design that could be RFC’d. In this first post, I want to talk about one of the interesting questions, what I am going to call “internal” vs “external” proofs. I know that this material can seem abstract, so I’m going to try and connect it to “real Rust” as much as possible! This particular blog post is an introduction, explaining the general problem and giving some motivation for why we care.

What are cyclic trait implementations?

Right now in Rust we require most traits to have non-cyclic, or inductive, implementations. To explain what I mean, let’s consider this trait:

traitDump{fn dump(&self);}

Now imagine that we have an impl of this for i32:

// Impl I
implDumpfori32{fn dump(&self){println!("{self}");}}

A simple impl for Rc<T> and `Option:

// Impl RC
impl<T>DumpforRc<T>whereT: Dump,{fn dump(&self){T::dump(self)}}// Impl Opt
impl<T>DumpforOption<T>whereT: Dump,{fn dump(&self){ifletSome(v)=self{T::dump(v)}}}

and finally a recursive List type that has an impl as well:

struct List<T>{value: Rc<T>,next: Option<Rc<List<T>>>,}// Impl L
impl<T>DumpforList<T>whereT: Dump,{fn dump(&self){Dump::dump(&self.value);ifletSome(n)=&self.next{Dump::dump(n);}}}

If I try to show that List<i32>: Dump, I do that by

  • Applying “impl L” to show that List<i32>: Dump if i32: Dump
    • Then applying “impl I” to show that i32: Dump

There’s no cycle here – that is, I didn’t have to use impl L to show that impl L is valid.

Cyclic logic sounds bad, but it can be exactly what you want

Now, when I said that “the impl L didn’t have to use the impl L to show that it is valid” that might not have sounded suspicious to you. In fact, it’s a pretty natural idea. After all, generally when you try to establish a logical argument, you aren’t allowed to use cyclic reasoning. That is, you can’t say: I know that Niko likes Rust because Niko likes Rust. So, in the same sense, it seems natural that I should not be able to say “I know that List<i32> implements Dump because List<i32> implements Dump”.

But actually, it would sometimes be really useful to say exactly that. One example is so-called “perfect derive”. In our Dump impl above, we had one where-clause, T: Dump. And if you were to create a custom derive for Dump and write #[derive(Dump)], the impl I showed is typically exactly what you would get. But it’s not necessarily what you want. Consider what you get with #[derive(Clone)]:

// Impl LC1
impl<T>CloneforList<T>whereT: Clone,// <-- generated but not really required!
{fn dump(&self){List{value: Clone::clone(&self.value),next: Clone::clone(&self.next),}}}

Here, the derive is going to create an impl that requires T: Clone. But if you look closely, you’ll see that all the fields only use Rc<T>, so in fact, we should be able to clone a List even without T: Clone! But how is the compiler to know this?

You might think that the compiler could do some super smarty-pants analysis on the fields to figure it out. And, in a way, it can: that is what cyclic trait solving is all about. The thing is, while the compiler can do that, the derive cannot – the derive doesn’t have access to the definitions of other types and so forth, and clearly we would need to know things about Option and Rc to figure out whether T: Clone is required here.

But what we could do is to generate a different impl. Instead of adding T: Clone for each type parameter, we could add a where-clause for each field type. This makes sense: after all, we are just going to be calling Clone on every field, so it’s quite logical to say that the impl is valid if every field is cloneable:

// Impl LC2
impl<T>CloneforList<T>whereRc<T>: Clone,Option<Rc<List<T>>>: Clone,{// .. as above ..
}

Under this formulation, we can see that all we have to be able to do is to clone an Rc<_> and clone an Option<Rc<_>>, neither of which require that T: Clone.

This idea is called perfect derive

We call this idea [perfect derive][] and it’s been a goal for a while. The thing is, cyclic reasoning is tricky to get right. The Clone example is actually an easy one: that one doesn’t really require cyclic reasoning:

  • To show that List<i32>: Clone we have to show that…
    • Rc<i32>: Clone, which is easy because impl<T> Clone for Rc<T> doesn’t have any where-clauses1
    • Option<Rc<List<i32>>>: Clone uses the impl<T> Clone for Option<T> where T: Clone impl which requires…
      • Rc<List<i32>>: Clone, which is again easy

But it’s not so easy for Dump

But if we use that same “cyclic derive pattern” to generate our Dump impl, things don’t work out so well. Instead of just a T: Dump bound, our Dump impl now has two bounds:

// impl L1
impl<T>DumpforList<T>whereRc<T>: Dump,// <-- Used to be `T: Dump`
Option<Rc<List<T>>>,// <-- This one is new!
{fn dump(&self){...}}

Now imagine we try to show List<i32>: Dump. We begin by applying impl L1, which requires us to show that its where clauses hold:

  • To show List<i32>: Dump we use impl L1, which has two where-clauses:
    • Rc<i32>: Dump, this one is easy because the Rc impl requires that i32: Dump which is true.
    • But Option<Rc<List<i32>>>: Dump is tricky. The Option impl requires that…
      • We need to prove Rc<List<i32>>: Dump, and then the Rc impl requires that…
        • We need to prove List<i32>: Dump, but that is what we started with! That’s cyclic logic!

Ugh. Something’s tricky here!

We can’t just accept any old cycle because of supertraits

Now, maybe you think we can just accept any cycles. And for these examples, it would be fine: but it’s not correct if you consider supertraits. Consider this trait and impl pair:

traitMagic: Copy {}// Impl M
impl<T>MagicforTwhereT: Magic,{}

If you are naive, this weird trait-impl pair can be used to prove that any type is Copy, regardless of whether it has a Copy impl. For example:

  • Say we want to prove that String: Copy. We observe that if a type implements Magic, it must implement Copy, so…
    • We begin by proving String: Magic. We use the impl M, which requires
      • that we show String: Magic, which is a cycle, so we accept it.

Uh oh, now we did something wrong. We proved that String: Copy even though there is no Copy impl. Something is fishy.

Now clearly we can all see the problem here – the implementation of Magic didn’t really add any information. It was just a tautology, saying that T: Magic if T: Magic. It’s not wrong, but implementing Magic was supposed to tell us more than just the fact that there is an impl of Magic, it was supposed to tell us also that the supertrait Copy is implemented. And that’s not true here.

But if you think about it, it’s hard to decide why we should reject impl M but accept the impl L1 of Dump for List<T>. They both wind up with a cyclic proof. So what’s the difference? This is the question we’ll be exploring over the next few blog posts.

Soundness for traits

This gets at an interesting question: what does it mean for the trait system to be sound. This seems obvious but actually it was a question I found kind of non-obvious for a long time.

We’ve found two satisfactory answers to that question. One of them involves converting to dictionary-passing style. Nadri explained that in a blog post. I think that’s a great post to read. I’m going to give another definition here that doesn’t require converting to a dependently typed program2

My rough definition is this3: the trait system is sound if, whenever it accepts some program P, that program cannot have a function that believes some Trait holds for the T, but there is no impl of Trait that can be used. So in the case of Magic and Copy, it’s easy to write a program that shows simple cyclic trait solving is unsound:

traitMagic: Copy {}impl<T: Magic>MagicforT{}fn is_copy<T: Copy>(){// this function believes `T: Copy`
}fn main(){// this can be called because we believe that
// * `String: Magic` because
//     * `String: Magic`, and we accept cycles.
// And then `String: Magic` implies `String: Copy`.
is_copy::<String>();}

By my definition, any sound type/trait system must reject this program because, if it were to execute, then execution would reach is_copy::<String> and yet there is no Copy impl that is judged to ber applicable to String. Uh oh!

Coming next

As I promised, this post was mostly focused on “setting the scene”. My goal was to explain what the problem is that we are trying to solve – permitting “good cyclic impls” but forbidding bad ones. I didn’t spend a lot of time on the bad ones, but it turns out that there’s a wide variety of unsound things one can do, some of which the compiler currently gets wrong, others of which it would only get wrong if we started permitting cycles.

My motivation for getting into this work is a bit complicated. I want perfect derive. But it’s also a loose end in our trait semantics that I really want to see nailed down before we move onto other tasks. Having auto traits (e.g., Send) work differently from other traits is clearly a “smell”, and without a strong understanding of the logical underpinnings of our trait system it’s easy to get things wrong when we build extensions.

In the next few posts I’ll go a bit deeper into the exploration I and others have been doing. I’ll talk about some of the “false starts” we took along the way and why they don’t work, and then about some of the solutions that are under consideration. Working through this stuff has really helped me to broaden my understanding of various areas of logic. By the time we’re done, we’ll cover4 coinduction and productivity, modal logic and the later modality, and we’ll see how our techniques might even help us with resolving specialization5.

I cited it earlier, but if you want to read other tasks on the same subject, I definitely recommend Nadri’s post on dictionary-passing style.


  1. Apart from the default Sized bound, I’m ignoring that here ↩︎

  2. I have found that both the dictionary-passing interpretation and the logic approach I’m using are valuable. In the end, they’re more or less equivalent, which I guess shouldn’t be surprising if you’ve heard of the Curry Howard Correspondence, but I’ll talk about that later perhaps. ↩︎

  3. I would like to, but haven’t, define a simplified version of Rust that includes trait solving and simple type checkoing and show that it cannot “go wrong”. ↩︎

  4. In a shallow way, I’m no expert! ↩︎

  5. Plot twist, bet you didn’t see that coming! I sure didn’t. ↩︎

Ludovic Hirlimann

20+ years of running "alpha quality" browser as my main web browser

 I have been accessing, using the internet since probably around 1991, using my dad's academic account and then schools and then home. I've used the internet before the web existed, sending emails, downloading freeware and sharewares from "huge" ftp repository. Sharing ftp site, using Usenet to find more and share more. This was a time you had to buy Ethernet NuBus card and install both Ethernet drivers and TCP/IP ones.

Then one day something called NCSA Mosaic came into the picture - this changed the way I consumed 'the network'. After a while some friends showed me Netscape, and mosaic faded away. I've used Netscape on Mac, Windows 3.11, NT, on workstations running Unix (hello Dec Alpha).

At the time my gig was either running OS/2, and Linux Slackware on my Pentium machine. I then got into the Beos world, and that was one OS that Netscape didn't ship binaries for. Microsoft had started pushing Internet explorer (IE). I was starting to work and *had* to use Windows. I came up with the idea of using Mozilla at work in order to find bugs that would later be fixed when I'd use it home on my favorite OS.

So I started using Mozilla builds for windows, and then following planet and mozilla.org to be aware of the process. I would now and then file bugzilla issues, often to discover they've been filled already :-) I clearly remember the day tabs were introduced. I walked around the room I was working in showing the great new feature to my colleagues that were using Mozilla. There were no formal releases, at the time CI-CD didn't exist completely yet, so you could keep the same version for a day or for a week depending on how broken the tree was

 

Then I continued using builds until updates became available, and I choose to use the nightly channel. At that time my motivation had changed, I had moved from dead BeOS to NeXTStep+1 aka Mac OS X. There was no way for me to help My then browser of choice Camino at work. Mac OS X was way less popular in the tech industry that it is now.

The web landscape was dominated by IE , the mac version was unfortunately different meaning that using it made a bunch of websites unusable. 

When Firefox got out, Camino started dying and I switched boats.

I then got employed by Mozillamessaging and used both Thunderbird and Firefox nightlies. I was the QA lead and there were no way I could test all the software and hardware combination used by our users. So crowd testing was very valuable to me.  At this point I had stopped reporting issues and the only thing I was reporting were crashes. 

Since 2010 a bunch of Chrome related browsers have started taking the web over, a bit like in 2006 when Microsoft was running the show, instead now google is.  I currently run 2 versions of Firefox, nightly for personal stuff and release for work related ones. I occasionally have to fire a chrome related browser because testing with Ff from the vendors is not being done (I'm looking at DocuSign and FoundryVtt ).

So running nightly software has not been a burden at all, because there are a few crashes here and there, but I always submit, so the stability team at Mozilla can investigate. The idea is that if it crashes on me, it won't crash on my Dad's computer because he runs releases. Once or twice in the course of those 20 years did I have to either reinstall or create a new profile. That's how stable nightly has been for me.

I'm annoyed at some of the decision Mozilla makes, but again they are the most independent browser maker out there with a big enough team to deal with the development surface of a browser. Hence, for some forseable future I'll be running nightlies in hopes of keeping the web open. I'm lurking at Servo and following it's development, reporting issues there because I see it has an alternative to Firefox, but it's far from being ready yet.

I'd encourage some of my readers to try/use Nightly. If you choose to do so, send in crash reports and enable telemetry.

The Servo Blog

June in Servo: real world compat, media queries, SharedWorker, and more!

Servo 0.4.0 contains all of the changes we landed in June, which came out to yet another record 558 commits (April: 534, May: 391). For security fixes, see § Security.

We’ve shipped several new web platform features:

Plus a bunch of new DOM APIs:

This is another big update, so here’s an outline:

You can help!

Servo is steadily becoming a bigger and busier project every month, and by June 2026, we’ve been reading through over four times the commits as we did when we started in September 2023.

This is hard work, particularly since there are things we need to know that are often difficult to answer just by reading the changes:

  • Who does the change affect, if anyone? Does it affect users, Servo developers, embedders, or some other group?

  • What observable difference does the change make, if any?

  • Does the feature require any preferences to be enabled, or is it enabled for everyone by default?

  • Are any real-world websites affected by the change?

  • What issue or broader project is the change related to? This question is answered by Fixes: #xxxxx or Part of: #xxxxx in the PR description.

Thanks to an initiative by @jdm, it’s now easier than ever for you to help us answer those questions, using the Servo Highfive bot! If you’re working on a pull request that you think might be interesting for the next monthly update, even if you’re not 100% sure, tell us about it by following the steps below:

  1. You add the monthly update label to your pull request, or comment @servo-highfive monthly update

  2. Highfive posts a comment asking you some questions

  3. You answer those questions in a comment containing @servo-highfive monthly update answer

Security

Servo’s JS runtime, SpiderMonkey 140.10.1, had several security bugs that have been fixed in Servo 0.4.0 with the update to SpiderMonkey 140.11.0 (@jschwe, #45584). For more details, see CVE-2026-8388, CVE-2026-8391, CVE-2026-8974, CVE-2026-8975, and MFSA 2026-48.

Several more security bugs in Servo’s JS runtime have been fixed in Servo 0.4.0 with the update to SpiderMonkey 140.12.0 (@jschwe, #45766). The exact CVEs that apply to us are not yet known, but for more details, see MFSA 2026-58.

RSA operations in Subtle­Crypto now do modular exponentiation in constant time (@kkoyung, #45631). Please note that our RSA implementation is currently vulnerable to the Marvin Attack – for more details, see RUSTSEC-2023-0071.

ML-DSA operations in Subtle­Crypto now do the Decompose step in constant time, fixing RUSTSEC-2025-0144 (@kkoyung, #45294).

We’ve fixed an HTML injection bug (XSS) in file:/// directory listings, which affected file names containing </script> (@sahvx655-wq, #45510).

Real world compat

Layout correctness has significantly improved on lichess.org, and many websites have become a lot more readable thanks to our improved handling of variable fonts (@simonwuelker, #45768), including Zulip (servo.zulipchat.com) and Speedtest (speedtest.net).

v0.3.0
v0.4.0
lichess.org
v0.3.0
v0.4.0
Zulip (servo.zulipchat.com)
v0.3.0
v0.4.0
Speedtest (speedtest.net)

Many websites worked in Servo even before version 0.4.0, including Google Photos (photos.google.com) and Cash Converters (cashconverters.com.au), and continue to work in version 0.4.0. Other websites, like Google Maps (maps.google.com) and OpenStreetMap (www.openstreetmap.org), render well but have some issues with interactivity.

Google Photos (photos.google.com)
Cash Converters (cashconverters.com.au)
Google Maps (maps.google.com)
OpenStreetMap (www.openstreetmap.org)

We’re interested to hear how well your favourite websites run in Servo! Report successes in this Zulip thread, and failures in our GitHub issues.

Work in progress

We’re implementing the more powerful version of ‘attr()’ that can be used anywhere, not just in ‘content’, under --pref layout­_css­_attr­_enabled (@Loirooriol, #45041, #45421, #45495, #45752).

WebGPU support has improved, under --pref dom­_webgpu­_enabled:

  • implemented copy­External­Image­To­Texture() on GPU­Queue (@sagudev, #45646)
  • implemented create­Query­Set() on GPU­Device and resolve­Query­Set() on GPU­Command­Encoder (@sagudev, #45644)
  • implemented push­Debug­Group(), pop­Debug­Group(), and insert­Debug­Marker() on GPU­Command­Encoder, GPU­Compute­Pass­Encoder, and GPU­Render­Pass­Encoder (@jschwe, #45489)
  • more conformant GPU­Texture (@sagudev, #45300)
  • more conformant request­Adapter() on GPU (@sagudev, #45424)
  • more conformant secure context enforcement (@sagudev, #45279)

All of the features above are enabled in servoshell’s experimental mode.

We’ve made more progress towards accessibility support, under --pref accessibility_enabled (@alice, @delan, #45555, #45554, #44949).

We’ve started implementing visible and interactive text selection (@mrobinson, @SimonSapin, #46107), one of the most long-awaited features of any web browser. Stay tuned!

We’ve also started working on Web Animations, under --pref dom­_web­_animations­_enabled (@simonwuelker, #45522, #45983), as well as webkit­Relative­Path on File, under --pref dom­_entries­_api­_enabled (@yezhizhen, #45666).

Rust doesn’t have a stable ABI, so it has generally not been possible to embed Servo in another application without building Servo from source. To make it possible, we’ve started designing a wrapper C API that will let you consume Servo as a prebuilt shared library using the stable and ubiquitous C ABI (@mukilan, #44984). Eventually the idea is that we’ll create a wrapper Rust API around that wrapper C API, so you can have both the ergonomics of Rust and the build simplicity of C.

Embedding API

New in the Servo API:

Breaking changes:

  • Web­View::send­_error has been removed (@mukilan, #45502) – this method was always meant to be internal, and has become unused after we introduced the new Web­View- and Web­View­Delegate-based API

We’ve improved the docs for Web­View, Web­View­Delegate, JS­Value, Alert­Dialog, Allow­Or­Deny­Request, Authentication­Response, Bluetooth­Device­Description, Confirm­Dialog, Console­Log­Level, Create­New­Web­View­Request, Embedder­Control, Embedder­Control­Response, File­Picker, Image, Java­Script­Error­Info, Navigation­Request, Permission­Request, Pixel­Format, Prompt­Dialog, Protocol­Handler­Registration, Protocol­Handler­Update­Registration, Scroll, Select­Element, Select­Element­Request, and Web­View­Vector (@mukilan, #45282, #45467).

For users and developers

In servoshell:

  • the Android version now requires Android 13+ (@jschwe, #46104)

  • the desktop version now lets you drag and drop files to open them (@simonwuelker, #45454)

  • the desktop version now lets the tab bar scroll horizontally if you have too many tabs open, but from one tab hoarder to another, maybe you should reconsider having so many tabs open (@Nylme, #44884)

  • the desktop version enters fullscreen on the monitor containing the window, even if you’ve moved it to a different monitor (@rhit-kapilaar, #45556)

  • the desktop UI is more performant, resizes more smoothly, and no longer gets stuck in hovered states (@mrobinson, #45289, #45456, #45290)

  • <select multiple> should now be interactable on all desktop platforms (@alexcat3, #45419)

  • localhost:<port> now implies http:// in the location bar and on the command line, rather than treating localhost: as an unsupported URL scheme (@SteveSharonSam, #45729, #45832)

When using the Firefox DevTools:

  • in the Console tab, uncaught exceptions are reported correctly (@jdm, #45549)

  • in the Console and Debugger tabs, you can now inspect the elements of nested arrays and the entries of Map objects (@atbrakhi, #45435, #45514, #45767)

  • in the Debugger tab, the Scopes panel now shows any ‘(uninitialized)’ variables, the value of this, and the global scope (@atbrakhi, @eerii, #45824, #45517)

We’ve fixed some build issues on riscv32, riscv64, and arm64 (@fxzjshm, @saschanaz, #45285, #45731), and modernised servoshell for Android to use Compose UI and Kotlin (@veyndan, #45923, #45932, #45941, #45982, #45985, #46015, #46035, #46037, #46046, #46053, #46061, #46071, #45641, #45643, #45650, #45665, #45671, #45676, #45679, #45683, #45712, #45713, #45734, #45738).

For developers of Servo itself:

  • mach try --help now lists all of the kinds of try jobs you can run (@shubhamg13, #45607)

  • mach test-wpt --update-expectations lets you run Web Platform Tests and update expectations in a single command (@TimvdLippe, #45521), rather than having to run mach test-wpt --log-raw <path> followed by mach update-wpt <path>

More on the web platform

To allow for more performant scrolling, ‘wheel’ events are no longer .cancelable unless there are one or more non-passive event listeners (@kunalmohan, #45667). Note that like in Firefox, ‘wheel’ events are passive by default.

‘dotted’, ‘dashed’, and ‘wavy’ text decorations are now continuous across element boundaries (@mrobinson, #45726).

We’ve improved the conformance of <dialog> (@skyz1, @mrobinson, #45825, #45761), <iframe sandbox> (@cychronex-labs, #45880), <input minlength> and <input maxlength> (@skyz1, #45705), CSS gradients (@mrobinson, #43945), ‘font-style’ and ‘unicode-range’ in ‘@font-face’ (@Loirooriol, #45821), FontFaceSet (@mrobinson, #45390, #45382), HTML­Input­Element (@steigeo, #45416), Intersection­Observer (@jdm, #45655, #45659, #45680), new Response() (@yezhizhen, #45953), URL.create­Object­URL() and URL.revoke­Object­URL() (@yezhizhen, #45182, #45417), and ECDSA and Ed25519 in Subtle­Crypto (@kkoyung, #45833, #46017).

We’ve fixed bugs related to <input hidden> (@mrobinson, #45750), ‘animation-delay’ (@yezhizhen, #45013), ‘clip-path’ (@Loirooriol, #45468, #45373), ‘tab-size’ (@SimonSapin, @mrobinson, #45309), ‘width’ and ‘height’ (@RichardTjokroutomo, #44627), ‘box-shadow: inset’ (@Loirooriol, #45620), ‘animation­iteration’ events (@Loirooriol, #45990), ‘click’ events (@mrobinson, #45751), ‘load’ events (@jdm, #45883), ‘error’ events in Worker global scopes (@Gae24, #45829), and document­.get­Element­By­Id() (@mrobinson, #45433).

Garbage collection safety

We use a RefCell-based mechanism to store many of our DOM types in other DOM types, enforcing Rust’s “aliasing xor mutability” rule at runtime by panicking if the rule is violated. But when garbage collection happens, we need to borrow() each DomRefCell to trace the references, and this is the source of many panic bugs. To fix that whole class of bugs, we initially created CanGc, a marker type that would annotate the code paths where GC can occur, in conjunction with custom static analysis (@jdm, #33140).

With the Rust type system we can do even better, if we flip that around and require any borrow_mut() call to prove that GC can not occur by passing a NoGC marker value. We can then require that a &NoGC must be borrowed from a &JSContext (which blocks GC) and not a &mut JSContext (which allows GC), taking advantage of how Rust references work without needing any custom static analysis.

We have a large codebase that needs to be migrated in parts, so for now we’ve created the new method safe­_borrow­_mut() (@sagudev, #46050). We also need to update all of our script-related code to borrow our safe JSContext wrapper, rather than creating an owned JSContext on the spot.

This continues our long-running effort to use the Rust type system to make Servo’s integration with SpiderMonkey safer and more reliable (@Gae24, @Keerti707, @Narfinger, @TimvdLippe, @sagudev, @guptapiyush16, @ivomurrell, @kunalmohan, @skyz1, #45230, #45436, #45503, #45617, #45711, #45797, #45800, #45858, #45884, #45937, #45902, #45968, #45977, #45991, #46003, #46005, #46084, #45548, #45552, #45590, #45909, #45912, #45943, #46089, #46117, #46114, #45320, #45324, #45328, #45340, #45381, #45385, #45410, #45392, #45409, #45604, #45616, #45618, #45627, #45636, #45662, #45663, #45675, #45674, #45677, #45684, #45735, #45807, #45810, #45816, #45818, #45828, #45838, #45836, #45837, #45840, #45841, #45857, #45859, #45862, #45875, #45887, #45931, #45964, #45935, #45987, #45988, #46001, #46040, #46051, #46057, #46106, #46125, #45678, #46002, #45845, #45645, #45673, #45259, #45817, #45822, #45876, #45877, #45891).

Performance and stability

NoGC was designed to prevent dynamic borrow failures, but it also enables some performance optimisations! If we can prove that garbage collection is impossible in some part of Servo, we can often avoid rooting JavaScript objects when interacting with them within that region of code. This has allowed us to reduce overheads by over 1% in the layout process and in HTML­Collection (@Narfinger, #46092, #45582).

Our memory usage has improved, with BoxFragment now 17% smaller (288 → 240 bytes on amd64) and ShapeCacheEntry now smaller too (@SimonSapin, @mrobinson, @simonwuelker, #45183, #45496).

We’ve fixed some nasty memory leaks when reloading and in 2D canvases (@Taym95, @sagudev, @jschwe, #45455, #45261, #45414).

Speaking of which, 2D canvases now use up to 23% less power (@yezhizhen, #45301), and we now avoid rasterising the same SVG more than once (@Narfinger, @jschwe, #44805).

Servo now decodes all images asynchronously and fills image caches asynchronously, leaving script threads (web content processes) more time for other work (@Narfinger, #45542, #44483). On top of that, we’ve improved incremental layout (@mrobinson, @Loirooriol, #45411) and reduced reflows in IntersectionObserver (@jschwe, #45986).

We’ve started working on incremental updates for the stacking context tree, and as a side effect, we’ve made some layout-bound microbenchmarks up to 10% faster (@mrobinson, @Loirooriol, #45208).

We’ve also reduced allocations, copies, GC rooting steps, and other operations in many parts of Servo (@Narfinger, @SimonSapin, @mrobinson, @Loirooriol, #45506, #45969, #45940, #45760, #46090, #45335, #45413, #45511).

For several months, Frédéric (@fred-wang) has been fuzzing for Servo bugs, and thanks to his work we’ve fixed sixteen (16) crash bugs in June, affecting <iframe>, <slot>, <link onerror>, ‘animation’, ‘clip-path’, ‘content’, ‘rotate’, ‘transition’, ‘transform-style’, ‘display: contents’, ‘overflow: clip’, CSS­Keyframes­Rule, Font­Face, stop() on Window, document­.element­From­Point(), and the DOM tree (@mrobinson, @Loirooriol, @fred-wang, #46031, #46027, #46054, #46058, #46016, #46028, #46033, #45287, #45951, #45634, #45629, #46110, #46094, #45799, #45611, #45682, #45788, #45612, #45834).

We’ve also fixed crash bugs related to IPC failures, HTML­Input­Element, Range, the DevTools Debugger tab, and when servoshell is built with --features native-bluetooth (@jschwe, @Taym95, @mrobinson, @atbrakhi, @mukilan, #45311, #45619, #45765, #45513, #45702).

New contributors

A special thanks to the following people for landing their first patch in Servo:

Interested in helping build a web browser? Take a look at our curated list of issues that are good for new contributors!

Donations

Thanks again for your generous support! We are now receiving 7681 USD/month (+0.2% from May) in recurring donations. This helps us cover the cost of our speedy CI and benchmarking servers, one of our latest Outreachy interns, and funding maintainer work that helps more people contribute to Servo.

Servo is also on thanks.dev, and already 35 GitHub users (same as May) that depend on Servo are sponsoring us there. If you use Servo libraries like url, html5ever, selectors, or cssparser, signing up for thanks.dev could be a good way for you (or your employer) to give back to the community.

We now have sponsorship tiers that allow you or your organisation to donate to the Servo project with public acknowlegement of your support. If you’re interested in this kind of sponsorship, please contact us at join@servo.org.

7681 USD/month
10000

Use of donations is decided transparently via the Technical Steering Committee’s public funding request process, and active proposals are tracked in servo/project#187. For more details, head to our Sponsorship page.

Martin Thompson

A Whole New Protocol Makes No Sense

Today, the IETF held the CURRENT BoF, where the goal was to develop a new protocol. That protocol would be substantially like TLS, reusing its record layer and basic structure, but it would drop in MLS for key exchange.

This is somewhere between a pretty bad idea and a horrible idea.

The wholesale replacement of a huge chunk of protocol architectures is a hallmark of a lot of the AI-generated protocol proposals that have flooded the IETF. A small blemish is identified, then the fix is a whole new protocol, or a major piece of surgery. No regard for the wisdom of Chesterton’s Fence or the accumulated knowledge and usefulness embodied in what exists.

Experienced engineers know that rewriting a code module is not something you do lightly. There’s lots of literature out there about why this is a bad idea generally, and some emerging discussion about how AI might just change that.

The reasons not to rewrite a software component still largely apply to a protocol component. The reasons that AI might make it easier to do that safely, less so. Protocols are different.

Wholesale Change Will Miss Use Cases

Just like with a code change, a protocol component that changes will miss use cases that people really care about.

The usual concerns with code apply:

  • The existing features you know about and can test for can be handled.
  • The existing problems you know and care about can be fixed.
  • You inevitably introduce brand-new problems.
  • The existing features you don’t know about get lost.

Unlike code changes, you probably don’t have a test case for existing features that you didn’t know about. We found that with HTTP/2, where a number of use cases got lost in the process of “upgrading” HTTP.

In HTTP/1.1, performing client authentication in the middle of request was possible. Losing that capability in HTTP/2 affected few enough people that it was not badly damaging for the ecosystem. It still sucked.

A lot of work was done to try to find these issues, but we did not learn about these problems until fairly late in the process.

Proposing a protocol change means asking a whole lot of other people, many of whom are not invested in your goals, to do that work.

Changing a protocol by replacing a chunk of it, no matter how much care is taken, either asks the entire ecosystem to change with you.

That means asking everyone to move with you. If they don’t, you are not changing the protocol, you are forking it.

Forking A Protocol Destroys Interoperability

The real value of having a protocol like TLS is that a great many things can all talk to each other.

Forking a protocol – and sometimes profiling a protocol, a subject for another post – destroys that. You now have two ways to achieve the same goal, and a choice to join one of two clubs. You can join both, but that means constantly translating back and forth, something that can only get harder over time as protocol semantics diverge.

And yes, in case you were asking, this applies to the entirety of the IETF IoT sphere, which has parallel HTTP, TLS, and other analogues. Ostensibly, these address the needs of highly constrained hardware, but the cost is an ecosystem cut off from the mainstream.

But Fixing Protocols Is Hard

Yes, existing protocols come with baggage or technical debt. Maybe they aren’t perfectly optimized for your use.

The value that an existing protocol carries is that you are sharing the burden of its maintenance with a great many more people. Fixing it, maybe by adding extensions to support your needs, comes with opportunities to improve the protocol even beyond that immediate need. Every change is a chance to work off some of the accumulated cruft.

Major refreshes, like the TLS 1.3 reworking, cleared out a ton of cruft in the process. You get to benefit from the work that others do to improve that protocol too.

Do the Work

It is hard to be a responsible steward for the fabric of the Internet. We do it because it is worthwhile. Ignoring the lessons of the past is not helpful.

AI makes code cheap, but it doesn’t make standards cheap. That is because standards are agreements between people and those take time to build.

David Burns

Testing Shouldn't Be a Chore. Here is How to Make it Automatic.

Let’s be honest: nobody wakes up excited to do chores.

For a lot of engineering teams, testing feels exactly like that, a tedious, manual chore tacked onto the end of a long sprint. The developer finishes the feature, throws it over the wall, and prays it doesn’t bounce back with a list of bugs.

When testing feels like an extra burden, people skip it, rush through it, or resent it. And that’s exactly when critical issues slip through into production.