Looking For

Firefox Nightly

Backup for a Rainy Day – These Weeks in Firefox: Issue 202

Highlights

  • The profile backup mechanism has been enabled by default for all desktop platforms in Nightly, as well as Beta! The current plan is to have this ride out to Firefox 151 for Windows, macOS and Linux on May 18th!
    • This feature, when enabled, will create a copy of your profile data in the background and store it in a single file on your file system that you can restore from.
    • You will be able to manage this feature in Settings under Sync (for now)
    • You can read more about the feature here
  • As followups to the recent addition to the WebExtension tabs API to support the new SplitView tabs feature, tabs.group() and tabs.ungroup() have been fixed to work correctly with split view tabs, and fixed split views being prepended instead of appended to tab groups when adopted into a new window – Bug 2029099 / Bug 2029534
  • Adaptive autofill has been enabled on Nightly.
    • Previously, autofill only completed domains (e.g. typing red autofilled reddit.com). Now it can also complete full URLs for pages you visit often (e.g. red → reddit.com/r/firefox), learning from what you actually click in the address bar. If a suggestion isn’t helpful, you can now dismiss it so autofill learns what not to show you too.
  • Markus Stange [:mstange] implemented dynamic toolbar on top in RDM (#1978145), but also implemented some static skeleton UI so it’s closer to what we actually have in Firefox for Android
    • dynamic toolbar is behind a pref: devtools.responsive.dynamicToolbar.enabled
    • it can be put on top by setting devtools.responsive.dynamicToolbar.onTop, otherwise it’s at the bottom

Friends of the Firefox team

Resolved bugs (excluding employees)

Script to find new contributors from bug list

Volunteers that fixed more than one bug

  • Amin Amir
  • aoia7rz7l
  • Chukwuka Rosemary
  • DrSeed
  • Frédéric Wang Nélar
  • japandi
  • John Iweh
  • jonathancabera
  • Josh Aas
  • Keji Bakare
  • kofoworola shonuyi
  • konyhéa
  • liz
  • Mathew Hodson
  • Okhuomon Ajayi
  • Oluwatobi
  • ROSHAAN
  • Sam Johnson

New contributors (🌟 = first patch)

 

Project Updates

Add-ons / Web Extensions

Addon Manager & about:addons
  • In preparation for the Project Nova restyling of the about:addons page, we have refactored about:addons into separate per-component ES modules, splitting the monolithic aboutaddons.js and aboutaddons.html into 16 dedicated component files under components/ (with no behavior or UI changes) – Bug 2032014
    • NOTE: if you have working on patches with changes to about:addons internals it is very likely you’ll need to rebase and solve merge conflicts hit on top of this refactoring, the internals are still largely the same as before but don’t hesitate to reach out to the Addons team if you have doubts / questions or need help to figure out how to adapt your patch of top of these changes
WebExtensions Framework
  • Fixed exportFunction to preserve the constructibility of the wrapped function instead of unconditionally making all exported functions implicitly as constructors – Bug 2033173
    • Thanks to Gregory Pappas for contributing this improvement to the Content Scripts’ Xray Wrappers helpers!
  • Fixed a Firefox 151 regression where extension content scripts accessing location.ancestorOrigins caused subsequent page script reads of the same property to fail with “Permission denied”, breaking sites like Gmail – Bug 2034329
    • Thanks to Simon Farre for promptly investigating and fixing this recent regression!
WebExtension APIs
  • Updated sessions.getRecentlyClosed() to remove the hardcoded cap when maxResults is omitted – Bug 1392125
    • Shoutout to Amine Zroual for contributing this enhancement to the sessions WebExtensions API!

DevTools

Fluent

  • We’re now at over 72% of our strings being Fluent! Got a component still using .properties? Convert when you can!

Migration Improvements

New Tab Page

Search and Urlbar

  • Marco has fixed a couple of issues with the places databases to try and improve stability. This should help with avoiding users losing bookmarks or favicons.
  • Work continues on the new separate search bar to improve the functionality, e.g. allowing middle click to perform a search in a new tab, avoiding performing a search when adding a search engine.
  • Work also continues on the new Nova layouts.

Smart Window

  • uplifted 10 bugs to 150.0.1 dot release addressing initial user feedback from diary study and Connect
    • jump to bottom of conversation 2028692
    • stop streaming button 2029204
    • back/forward navigation from assistant 2029229
    • dark mode for various chips 2024499
  • search engine switching from smart bar 2021973
  • Nova styling within smart window 2026794

Storybook/Reusable Components/Acorn Design System

UX Fundamentals

  • The error pages shown when a server sends back an invalid response header or an unsupported content encoding now display accurate, context-specific messages. The invalid response header page also gained a helpful list of next steps. – 2027209
  • In progress: The error page illustrations are being replaced with new artwork, and the system now supports per-illustration size configuration, giving each image the ability to define its own appropriate dimensions. – 2031837

Settings Redesign

Tom Ritter

webgl renderer privacy

WebGL exposes the details of your graphics hardware (specifically, the string that describes the rendering engine) in 2 ways. There are three levels of protection that browsers have taken to protect this data.

  1. gl.getParameter(gl.VENDOR) and gl.getParameter(gl.RENDERER) - these are the 'simple' names. At some point in the past, someone argued that it wasn't enough information, and therefore we have a second API
  2. let ext = gl.getExtension('WEBGL_debug_renderer_info'); and then gl.getParameter(ext.UNMASKED_VENDOR_WEBGL) and gl.getParameter(ext.UNMASKED_RENDERER_WEBGL)

The unmasked values are intended to be the more detailed ones, so always make sure you're comparing apples to apples. Another axis is that WebGL can render with Hardware or Software. This isn't a guarentee which one you'll get, but you can hint towards one or the other and the browser may or may not respect it. Here are your values:

Alright, now let's talk about what browsers do about it. There's no point in talking about Vendor, Renderer, and Unmasked Vendor - they don't really show as much detailed info, it's all about Unmasked Renderer. There are three levels:

  1. Give a constant value. (Or don't return anything at all.)
  2. 'Round' the values into buckets
  3. Give the exact value back

Safari and Tor Browser give constant values.

Firefox 'rounds'.

Chrome (and Brave, and I assume all-ish other Chrome-based browsers) give the exact value.

Firefox actually is purusing constant values, this week. I wrote this document for our QA team to test it. (You can get a sense of the internal sausage making it takes to launch a privacy feature from it.) I don't know if you can see the dates but I made it May 20th. The problem is this - websites use this data legitimately to adjust behavior so that users get the best experience possible. I found one example where they detect a buggy graphics stack; and a couple of examples where they adjust rendering so things are more performant for users with lower end machines - a problem Apple has less to worry about because they only support certain machine models!

A common response to this seems to be ambivalence, and I would suggest that is a bit elitist. Yes, if you're caring about the details reveal by a particular Web API you probably have a computer where you don't need to worry, but making the web work well for everyone is important for equitable access to improving everyone's human condition.

We have been bucketing WebGL Renderer since 2021. While many of our (supported, on-by-default) fingerprinting protections are part of Enhanced Tracking Protection - rolling out first in PBM/ETP Strict before making it to ETP Standard/Normal Browsing Mode - the bucketing is on by default, for everyone, and is not disabled if ETP is disabled.

How much of a difference does it make? A lot! Here is the distribution of the raw values. 83,705 distinct values.

WebGL Renderer Value Distribution, before bucketing

Compare that to the bucketed data. 131 distinct values.

WebGL Renderer Value Distribution, after bucketing

Now this data is from Firefox, so I cant say conclusively what the distribution of data is in other browsers, but... yeah. To claim Chrome (of all browsers!) is doing this better than us is pure FUD. We're making a big impact in how fingerprintable you are today and we're trying to improve it even further.

Nick Fitzgerald

A Structure-Aware Fuzzing Experiment

Structure-aware fuzzing can better exercise the system under test (SUT) by crafting inputs in the format expected by the SUT, rather than throwing pseudorandom bytes against it. That is, it avoids “shallow” inputs that the SUT will reject early (for example, syntactically invalid source text when fuzzing a programming language’s compiler) and only produces inputs that go “deep” into the SUT (e.g. programs that type-check and exercise the mid-end optimizer and backend code generator). The Rust fuzzing ecosystem is largely built around cargo-fuzz and the libfuzzer-sys crate, which provides two methods for structure-aware fuzzing:

  1. Generating structured inputs from scratch with the arbitrary crate

  2. Mutating existing inputs from the fuzzer’s corpus in a structure-aware manner, thereby producing new structured inputs, via the fuzz_mutator! hook

While the two methods are not technically mutually exclusive, combining the two can be difficult and engineering resources are finite. So:

If we are only implementing one approach, is generation or mutation better?

To help answer this question, I implemented structure-aware generation and mutation of guaranteed-valid WebAssembly (Wasm) instruction sequences. This task is small enough to be easily understandable but large enough and real enough to (hopefully) be representative and applicable to other domains, or, at the very least, interesting.1 To evaluate their effectiveness, I used Wasmtime as the SUT, libfuzzer-sys as the fuzzing engine driving everything, and then compared code coverage over time when using mutation-based fuzzing versus generation-based fuzzing.

Additionally, there are many ways we can generate pseudorandom WebAssembly instruction sequences. In this experiment, I’ve evaluated three methods:

  1. Unconstrained instruction sequence generation followed by a fixup pass to ensure validity

  2. Generating valid instructions in a forwards, bottom-up manner (from operands to operators)

  3. Generating valid instructions in a backwards, top-down manner (from operators to operands)

In contrast, while there are surely many ways to mutate a given WebAssembly instruction sequence into a new, valid instruction sequence, I’ve only implemented one method: perform an arbitrary instruction insertion, deletion, or replacement, producing a new but probably-invalid instruction sequence, and then run the same fixup pass mentioned previously to ensure validity. This is the direct mutation-based equivalent of the first generation-based method.

Before continuing further, I want to disclose that I am the author of wasm-smith and mutatis, and a maintainer of Wasmtime, arbitrary, libfuzzer-sys, and cargo-fuzz. That is, while I am familiar with Wasm, fuzzing, fuzzing Wasm, and both the arbitrary and mutatis crates, I may also be propagating my own biases into these implementations.

Background

Generation-Based and Mutation-Based Fuzzing

A generation-based fuzzer uses a generator to create a pseudo-random test cases from scratch, feeds these into the system under test, and reports any failures to the user:

fn generation_based_fuzzing<T>(
    // A test-case generator.
    generator: impl Fn() -> T,
    // A function to run the system under test with a
    // generated test case, returning a result that
    // describes whether the run was successful or
    // not.
    run_system_under_test: impl Fn(&T) -> FuzzResult,
) {
    loop {
        // Generate an input.
        let input = generator();

        // Run the input through the system under test.
        let result = run_system_under_test(&input);

        // If the system crashed, panicked, failed an
        // assertion, violated an invariant, or etc...
        // then report that to the user.
        if let Err(failure) = result {
            report_to_user(&input, failure);
        }
    }
}

On the other hand, mutation-based fuzzers are given an initial corpus of inputs and create new inputs by mutating existing corpus members. They run each new input through the SUT, report failures the same as before, and if the new input was “interesting” (for example, exercised new code paths in the SUT that weren’t previously covered in any other input’s execution) then the new input is added into the corpus for use in future test iterations:

fn mutation_based_fuzzing<T>(
    // A corpus of test cases.
    corpus: &mut Corpus<T>,
    // A function to pseudo-randomly mutate an existing
    // input into a new input.
    mutate: impl Fn(&T) -> T,
    // A function to run an input in the system under
    // test, returning a result that describes whether
    // the run was successful or not.
    run_system_under_test: impl Fn(&T) -> FuzzResult,
) {
    loop {
        // Choose an old test case from the corpus.
        let old_input = corpus.choose_one();

        // Pseudo-randomly mutate that old test case,
        // creating a new one.
        let input = mutate(old_input);

        // Run the input through the system under test.
        let result = run_system_under_test(&input);

        // If the system crashed, panicked, failed an
        // assertion, violated an invariant, or etc...
        // then report that to the user.
        if let Err(failure) = result {
            report_to_user(&input, failure);
        }

        // If the input was interesting, for example if
        // it executed previously-unknown code paths,
        // then add it into the corpus for use in a
        // future iteration.
        if result.input_was_interesting() {
            corpus.insert(input);
        }
    }
}

The two approaches are not mutually exclusive and hybrid generation- and mutation-based fuzzers exist.

More resources:

Structure-Aware Fuzzing

Structure-unaware fuzzing will generate pseudorandom byte sequences and pass them directly to the SUT. If the SUT expects some sort of structured input, e.g. the source text for a programming language, it is likely that these byte sequences are invalid and will be rejected early by the SUT’s frontend. For example, when fuzzing a compiler, the input is rejected as syntactically invalid by the parser or rejected as semantically invalid by the type checker. This can be useful when hardening a tokenizer, parser, or type checker, but is less useful when hunting for misoptimization in the mid-end or bad instruction encoding in the backend because the inputs are unlikely to make it that far through the compiler’s pipeline.

Structure-aware fuzzing will produce inputs that match the SUT’s expected input format. Returning to the compiler-fuzzing example, structure-aware fuzzing lets us generate valid programs for the compiler, so we can exercise more of the mid-end and backend, rather than just the frontend.

Structure-aware fuzzing is often generation-based: for example using grammar-based fuzzing to generate pseudorandom strings from a given language grammar or language-specific tools like csmith and wasm-smith that generate C and WebAssembly programs respectively. But structure-aware fuzzing can also be mutation-based: libFuzzer’s custom mutator example implements a structure-aware mutator for zlib-compressed strings, where the raw input is decompressed, the decompressed data is mutated, and then the mutated data is recompressed to provide the new raw input. The mutator is aware of the SUT’s zlib-compressed input structure.

More resources:

The arbitrary Crate

The arbitrary crate helps Rust developers write custom structure-aware generators for fuzzing. It provides building blocks and abstractions for translating a raw byte sequence (usually from a fuzzing engine) into a structured type, effectively interpreting the raw bytes as a “DNA string” or set of predetermined choices for its decision tree. The library also provides a derive(Arbitrary) macro to automatically implement its functionality for a given type.

Because arbitrary is effectively implemented by combining decision trees, it is extremely easy to create imbalanced trees and unintentionally bias the distribution of generated test cases.

The mutatis Crate

The mutatis crate is, at a high-level, performing the same role for authoring structure-aware mutators that arbitrary plays for generators. That is, it provides Rust developers with abstractions and combinators for creating custom structure-aware mutators. It also provides a derive(Mutate) macro to automatically implement its functionality for a given type.

mutatis is designed to resist bias via a two-phase design: first, it enumerates all of the candidate mutations that could be applied to a test case, and only afterwards chooses a particular random mutation from the candidate set to actually apply.

WebAssembly

WebAssembly is a virtual instruction set designed to be safe, portable, and fast. It is a stack machine where an instruction’s operands are popped off a stack during execution and results pushed. It has sandboxed linear memories, global variables, and local variables (the latter two effectively being two kinds of virtual registers). The following instruction sequence computes a * 3 and stores the result into memory at address p:

;; []
local.get $p
;; [p]
local.get $a
;; [p, a]
i32.const 3
;; [p, a, 3]
i32.mul
;; [p, a*3]
i32.store
;; []

Generator and Mutator Implementation

The range of all three generators and the mutator is the same universe of WebAssembly programs. They are all implemented on top of the same Module and Inst types, and, given enough time, none is capable of producing an instruction sequence that another cannot. This helps ensure that our comparison is apples-to-apples. However, due to their different implementation techniques, they do produce different distributions of WebAssembly programs within that universe, and produce test cases at different speeds from one another, which ultimately affects how efficiently they exercise the SUT.

All of the generators are built on top of the arbitrary crate. The mutator is built on top of the mutatis crate.

The Module type is our structured fuzzing input. It describes a WebAssembly module containing a variable number of linear memories, a variable number and type of globals, and one function with a variable number and type of parameters and results and a variable instruction sequence:

/// A WebAssembly module of the shape:
///
///     (module
///       (memory ...)
///       (memory ...)
///       ...
///
///       (global ...)
///       (global ...)
///       ...
///
///       (func (export "run") (param ...) (result ...)
///         ...
///       )
///     )
pub struct Module {
    num_memories: u32,
    globals: Vec<Global>,
    param_types: Vec<ValType>,
    result_types: Vec<ValType>,
    instructions: Vec<Inst>,
}

The Inst type is an enum of all the WebAssembly instructions the implementations support, which is all of the integer, float, SIMD, memory, local, and global instructions. Control-flow, threading, table, and GC instructions are not supported. Here is a subset of Inst’s definition:

/// A WebAssembly instruction.
pub enum Inst {
    Drop,
    LocalGet(u32),
    GlobalGet(u32),

    // ...

    I32Const(i32),
    I32Add,
    I32Sub,
    I32Mul,

    // ...

    I64Const(i64),
    I64Add,
    I64Sub,
    I64Mul,

    // ...

    F32Const(f32),
    F32Add,
    F32Sub,
    F32Mul,

    // ...

    F64Const(f64),
    F64Add,
    F64Sub,
    F64Mul,

    // ...

    I32WrapI64,
    I64ExtendI32S,
    I64ExtendI32U,

    // ...

    V128Const(i128),
    I8x16Add,
    I8x16Sub,

    // ...

    I32Load(u32),
    I64Load(u32),

    // ...

    I32Store(u32),
    I64Store(u32),

    // ...

    MemorySize(u32),
    MemoryGrow(u32),
}

There is an Inst::operand_types method that returns the types that the instruction pops from the stack, and an Inst::result_type method that returns the type of the value that the instruction pushes onto the stack, if any. Finally, the Module::to_wasm_binary method encodes the module into WebAssembly’s binary format, so it can be fed into Wasmtime. These methods are used, directly or indirectly, in every generator and mutator implementation.

arb

The arb generator leverages derive(arbitrary::Arbitrary) on our structured input types to generate a pseudorandom instance of Module, unconstrained by validity. The module’s instruction sequence is almost certainly not valid at this point: it likely is missing operands for instructions, producing more results than the function’s signature describes, producing results of types that don’t match the function signature, accessing globals and locals that don’t exist, etc… Having produced an instance of Module, it next calls the Module::fixup method to mutate the Module so that it is valid.

The fixup method works by abstractly interpreting the instruction sequence to track the types of each value on the stack at every program point. Whenever an instruction’s operand types don’t match the types on top of the stack, it generates dummy values of the correct type. When the instructions produce more values than the function’s signature proscribes, it emits drop instructions.

impl Module {
    pub fn fixup(&mut self, mut make_value: impl FnMut() -> i64) {
        // ...

        // The fixed-up instructions.
        let mut fixed = Vec::with_capacity(
            self.instructions.len(),
        );

        // The types on the stack at any given program
        // point. Similar to the Wasm spec's appendix's
        // validation algorithm.
        let mut stack: Vec<ValType> = Vec::new();

        for inst in mem::take(&mut self.instructions) {
            // Special-case `drop` because it is
            // polymorphic.
            if matches!(inst, Inst::Drop) {
                if stack.is_empty() {
                    fixed.push(
                        ValType::I32.make_const(make_value()),
                    );
                } else {
                    stack.pop();
                }
                fixed.push(inst);
                continue;
            }

            // First clamp entity indices to valid
            // ranges.
            let Some(inst) = self.fixup_inst_immediates(
                &mut make_value,
                has_mutable_global,
                inst,
            ) else {
                continue
            };

            // Then make sure that the stack has
            // operands of the correct types for this
            // instruction.
            self.fixup_stack(
                &mut make_value,
                &mut fixed,
                &mut stack,
                &inst,
            );

            // Finally, apply the effects to the stack.
            let len_operands = inst.operand_types(
                &self.globals,
            ).len();
            stack.truncate(stack.len() - len_operands);
            stack.extend(inst.result_type(
                &self.param_types,
                &self.globals,
            ));

            fixed.push(inst);
        }

        // ...

        self.instructions = fixed;
    }

    fn fixup_stack(
        &mut self,
        mut make_value: impl FnMut() -> i64,
        fixed: &mut Vec<Inst>,
        stack: &mut Vec<ValType>,
        inst: &Inst,
    ) {
        let needed = inst.operand_types(&self.globals);
        let n = needed.len();

        if stack.len() >= n {
            if (0..n).all(|i| {
                stack[stack.len() - n + i] == needed[i]
            }) {
                // All needed operands are on the stack.
                return;
            }
        } else {
            if stack.iter().enumerate().all(|(i, ty)| {
                *ty == needed[i]
            }) {
                // A prefix of needed operands are on the
                // stack; make constants for the tail that
                // are missing.
                for ty in &needed[stack.len()..] {
                    fixed.push(ty.make_const(make_value()));
                    stack.push(*ty);
                }
                return;
            }
        }

        // Otherwise, just make constants for all the
        // needed operands.
        for ty in needed {
            fixed.push(ty.make_const(make_value()));
            stack.push(*ty);
        }
    }

    // ...
}

The fixup method also makes sure that for all instructions that have an immediate referencing some entity, the referenced entity is valid. For example, for a local.get $l instruction, it ensures that local $l actually exists or else rewrites the local to one that does exist.

impl Module {
    // ...

    fn fixup_inst_immediates(
        &mut self,
        mut make_value: impl FnMut() -> i64,
        has_mutable_global: bool,
        mut inst: Inst,
    ) -> Option<Inst> {
        match &mut inst {
            Inst::LocalGet(l) => *l %= self.param_types.len() as u32,

            // ...

            Inst::I32Load(m)
            | Inst::I64Load(m)
            | Inst::F32Load(m)
            | Inst::F64Load(m)
            | Inst::V128Load(m) => {
                if self.num_memories == 0 {
                    return None;
                }
                *m %= self.num_memories;
            }

            // ...

            _ => {}
        }

        Some(inst)
    }
}

After calling fixup, the arb generator invokes Module::to_wasm_binary to get the encoded Wasm program.

bottom_up

The bottom_up generator also uses abstract interpretation to track the types of values on the stack. It generates instructions in forwards order, from operands to operators. It begins with an empty stack, filters candidate instructions down to just those that would be valid given the types currently on the stack, randomly chooses one, updates the stack types accordingly, and repeats the process. This is the same approach that wasm-smith uses. After generating instructions this way, it then makes sure that the final types on the stack match the function signature’s results, similar to the end of fixup.

impl Module {
    pub fn bottom_up(u: &mut Unstructured<'_>) -> Result<Self> {
        // ...

        let max_insts = u.int_in_range(1..=MAX_INSTS)?;
        let mut instructions = Vec::new();
        let mut stack: Vec<ValType> = Vec::new();

        for _ in 0..max_insts {
            if stack == result_types && u.ratio(3, 4)? {
                break;
            }

            // Choose a random instruction whose operand
            // types match those currently on the stack.
            let inst = choose_inst_bottom_up(
                u,
                &stack,
                &param_types,
                &globals,
                num_memories,
            )?;

            // Apply this instruction's effects to the
            // stack.
            apply_inst(
                &inst,
                &mut stack,
                &param_types,
                &globals,
            );
            instructions.push(inst);
        }

        // ...

        Ok(Module {
            param_types,
            result_types,
            globals,
            num_memories,
            instructions,
        })
    }
}

fn choose_inst_bottom_up(
    u: &mut Unstructured<'_>,
    stack: &[ValType],
    param_types: &[ValType],
    globals: &[Global],
    num_memories: u32,
) -> Result<Inst> {
    // Build up all the valid candidate instructions.
    let mut candidates: Vec<Inst> = Vec::new();

    // Producers are always okay: [] -> [t]
    candidates.push(Inst::I32Const(0));
    candidates.push(Inst::I64Const(0));
    candidates.push(Inst::F32Const(0.0));
    candidates.push(Inst::F64Const(0.0));
    candidates.push(Inst::V128Const(0));
    if !param_types.is_empty() {
        candidates.push(Inst::LocalGet(0));
    }

    // ...

    let top = stack.last().copied();
    let second = stack.get(stack.len() - 2).copied();

    // Drop needs 1 operand of any type: [t] -> []
    if top.is_some() {
        candidates.push(Inst::Drop);
    }

    // i32 unary: [i32] -> [...]
    if top == Some(I32) {
        candidates.push(Inst::I32Clz);
        candidates.push(Inst::I32Ctz);
        candidates.push(Inst::I32Popcnt);
        // ...
    }

    // i64 unary: [i64] -> [...]
    if top == Some(I64) {
        candidates.push(Inst::I64Clz);
        candidates.push(Inst::I64Ctz);
        candidates.push(Inst::I64Popcnt);
        // ...
    }

    // ...

    // i32 binary: [i32 i32] -> [...]
    if top == Some(I32) && second == Some(I32) {
        candidates.push(Inst::I32Add);
        candidates.push(Inst::I32Sub);
        candidates.push(Inst::I32Mul);
        // ...
    }

    // i64 binary: [i64 i64] -> [...]
    if top == Some(I64) && second == Some(I64) {
        candidates.push(Inst::I64Add);
        candidates.push(Inst::I64Sub);
        candidates.push(Inst::I64Mul);
        // ...
    }

    // ...

    // Choose a random instruction from the
    // candidates.
    let mut inst = *u.choose(&candidates)?;

    // If the instruction has immediates, generate
    // them here, as they were hard-coded during
    // candidate selection.
    match &mut inst {
        Inst::I32Const(v) => *v = u.arbitrary()?,
        Inst::I64Const(v) => *v = u.arbitrary()?,
        // ...
        Inst::GlobalGet(g) => {
            *g = u.int_in_range(0..=(globals.len() as u32 - 1))?;
        }
        // ...
        Inst::I32Load(m)
        | Inst::I64Load(m)
        | Inst::F32Load(m)
        | Inst::F64Load(m)
        | Inst::V128Load(m)
        | Inst::I32Store(m)
        | Inst::I64Store(m)
        | Inst::F32Store(m)
        | Inst::F64Store(m)
        | Inst::V128Store(m)
        | Inst::MemorySize(m)
        | Inst::MemoryGrow(m) => {
            *m = u.int_in_range(0..=(num_memories - 1))?;
        }
        _ => {}
    }

    Ok(inst)
}

After constructing a Module via bottom_up, we don’t need to call fixup because the module is already valid by construction, so all that’s left is invoking Module::to_wasm_binary to get the encoded Wasm program.

top_down

The top_down generator is very similar to bottom_up, but instead of generating instructions forwards, from operands to operators, it generates them backwards, from operators to operands. Instead of maintaining a stack of the types of values generated thus far by the instruction sequence prefix, it maintains a stack of the types of values expected by the instruction sequence suffix. This is the approach that rgfuzz by Park, Kim, and Yun takes.2

impl Module {
    pub fn top_down(
        u: &mut Unstructured<'_>,
    ) -> Result<Self> {
        // ...

        let max_insts = u.int_in_range(1..=MAX_INSTS)?;
        let mut instructions = Vec::new();
        let mut needed = result_types.clone();
        for _ in 0..max_insts {
            if needed.is_empty() && u.ratio(3, 4)? {
                break;
            }

            // Choose a random instruction in a
            // top-down manner.
            let inst = choose_inst_top_down(
                u,
                needed.last().copied(),
                &param_types,
                &globals,
                num_memories,
            )?;

            // Pop the result type from `needed`, if
            // any, as it's been satisfied.
            let ty = inst.result_type(
                &param_types,
                &globals,
            );
            if ty == needed.last().copied() {
                needed.pop();
            }

            // Add operand type demands.
            match &inst {
                Inst::Drop => {
                    // `drop` is polymorphic; choose
                    // a random type.
                    needed.push(u.arbitrary()?);
                }
                Inst::GlobalSet(g) => {
                    needed.push(globals[*g as usize].ty);
                }
                _ => {
                    needed.extend_from_slice(
                        inst.operand_types(&globals),
                    );
                }
            }

            instructions.push(inst);
        }

        // Fill remaining needed types with
        // constants.
        for ty in needed.iter().rev() {
            instructions.push(
                ty.make_const(u.arbitrary()?),
            );
        }

        // Instructions were generated backwards, so
        // reverse.
        instructions.reverse();

        Ok(Module {
            param_types,
            result_types,
            globals,
            num_memories,
            instructions: prefix,
        })
    }
}

fn choose_inst_top_down(
    u: &mut Unstructured<'_>,
    target_ty: Option<ValType>,
    param_types: &[ValType],
    globals: &[Global],
    num_memories: u32,
) -> Result<Inst> {
    let mut candidates: Vec<Inst> = Vec::new();
    match target_ty {
        Some(I32) => {
            candidates.push(Inst::I32Const(0));
            candidates.push(Inst::I32Add);
            candidates.push(Inst::I32Sub);
            candidates.push(Inst::I32Mul);
            // ...
        }
        Some(I64) => {
            candidates.push(Inst::I64Const(0));
            candidates.push(Inst::I64Add);
            candidates.push(Inst::I64Sub);
            candidates.push(Inst::I64Mul);
            // ...
        }
        Some(F32) => {
            candidates.push(Inst::F32Const(0.0));
            candidates.push(Inst::F32Add);
            candidates.push(Inst::F32Sub);
            candidates.push(Inst::F32Mul);
            // ...
        }
        // ...
        None => {
            // Nothing needed. `drop`, `global.set`, and
            // stores add demand.
            candidates.push(Inst::Drop);
            if globals.iter().any(|g| g.mutable) {
                candidates.push(Inst::GlobalSet(0));
            }
            if num_memories > 0 {
                candidates.push(Inst::I32Store(0));
                // ...
            }
        }
    }

    let mut inst = *u.choose(&candidates)?;

    // If the instruction has immediates, generate
    // them here, as they were hard-coded during
    // candidate selection. Same as `bottom_up`.
    match &mut inst {
        // ...
    }

    Ok(inst)
}

Similar to bottom_up, after we’ve constructed a Module via top_down, we don’t need to call fixup because the module is already valid by construction. All that’s left is invoking Module::to_wasm_binary to get the encoded Wasm program.

mutate

mutate is, as the name implies, a mutator rather than a generator. It is the direct equivalent of the arb generator, but for mutation: it uses derive(mutatis::Mutate) on Module and Inst to automatically generate custom mutators for these types, rather than authoring them by hand. After producing a new Module by mutating an old Module, that new Module probably represents an invalid Wasm program, in the same way that derive(arbitrary::Arbitrary) produces Modules that are probably invalid. And mutate also uses the same approach that arb does to resolve this problem: the fixup method.

But first, a mutator-specific wrinkle is that fuzz_mutator! gives us a mutable byte slice to mutate, not a Module. We address this gap by deriving the serde crate’s Serialize and Deserialize traits on Module and Inst, deserializing a Module from the mutable byte slice, mutating that deserialized Module with mutatis, and then reserializing it back into the mutable byte slice. We use the postcard crate here, but could just as easily use bincode, JSON, or protobuf.

use libfuzzer_sys::{fuzz_mutator, fuzz_target, fuzzer_mutate};

fuzz_mutator!(|
    data: &mut [u8],
    size: usize,
    max_size: usize,
    seed: u32,
| {
    // With probability of about 1/8, use default
    // mutator.
    if seed.count_ones() % 8 == 0 {
        return fuzzer_mutate(data, size, max_size);
    }

    // Try to decode using postcard; fallback to
    // default input on failure.
    let mut module: Module =
        postcard::from_bytes(&data[..size])
            .ok()
            .unwrap_or_default();

    // Mutate with `mutatis`.
    let mut session = mutatis::Session::new()
        .seed(seed.into())
        .shrink(max_size < size);
    if session.mutate(&mut module).is_ok() {
        if let Ok(encoded) = postcard::to_slice(
            &module,
            data,
        ) {
            return encoded.len();
        }
    }

    // Fallback to the default libfuzzer mutator if
    // serialization or mutation fails because, for
    // example, `data` doesn't have enough capacity.
    fuzzer_mutate(data, size, max_size)
});

Finally, the fuzz target itself deserializes the Module from the raw bytes, calls fixup, encodes it to a Wasm binary via Module::to_wasm_binary, and then passes that into Wasmtime.

fuzz_target!(|data: &[u8]| {
    let Ok(mut module) = postcard::from_bytes::<Module>(data) else {
        return;
    };
    module.fixup(|| 0);
    let wasm = module.to_wasm_binary();

    // ...
});

Benchmarking

Methodology

We pair each of our generators and mutator with libfuzzer-sys and feed the resulting test cases into Wasmtime. All fuzzers start with an empty corpus.

The most important metric for a fuzzer is its bug-finding ability, but that can be difficult to measure directly. For example, Wasmtime is actively fuzzed 24/7 with more-complete fuzzers than those implemented here, so, as expected, I have not found any bugs via these benchmarks. Therefore, instead of reporting a found-bugs count, the benchmark harness reports two alternative metrics:

  1. Coverage over time: Coverage is the cumulative code paths exercised by the fuzzer. A fuzzer cannot find bugs in code paths it does not cover. This is the most important metric reported.

  2. Executions over time: An execution is one iteration of the fuzzing loop. This is basically measuring how fast the fuzzer can produce test cases. All else being equal, more executions is better, but all else is rarely equal. It is easy to generate poor test cases very quickly: just return an empty sequence of Wasm instructions every time. Unfortunately, that exclusively leads to useless executions. Therefore, this metric is really only useful when comparing two implementations of the same algorithm, and I’ve omitted its results in the next section.

Additionally, I report results for both 24 hours of fuzzing and 5 minutes of fuzzing. The expected behavior of long-term fuzzing, e.g. 24/7 fuzzing in OSS-Fuzz, can be extrapolated from the 24-hour results. The 5-minute results show the expected behavior of short-term fuzzing, e.g. when using mutatis::check or arbtest.

Discussion of short-term fuzzing is somewhat rare, so I feel its motivation deserves explanation. I find short-term fuzzing useful in the following scenarios, for example:

  • Running a quick fuzzing session locally, to catch bugs that avoid detection in the traditional unit- and integration-test suites, before opening a pull request.
  • Running some quick fuzzing in CI before allowing a pull request to merge, for similar reasons.

That is, short-term fuzzing is useful for the same reasons and in the same scenarios as property-based testing.3

As recommended in Evaluating Fuzz Testing by Klees, Ruef, Cooper, Wei, and Hicks and adopted in Fuzz Bench: An Open Fuzzer Benchmarking Platform and Service by Metzman, Szekeres, Simon, Sprabery, and Arya, the benchmark harness tests the statistical significance of its results with a Mann-Whitney U-test. The harness performs 20 trials per fuzzer, the same number of trials as Fuzz Bench.

Results

24 Hours of Fuzzing
  • arb has 1.00 ± 0.00 times more coverage than bottom_up (p = 0.01)

  • mutate has 1.01 ± 0.00 times more coverage than arb (p = 0.00)

  • top_down has 1.00 ± 0.00 times more coverage than arb (p = 0.00)

  • mutate has 1.02 ± 0.00 times more coverage than bottom_up (p = 0.00)

  • top_down has 1.01 ± 0.00 times more coverage than bottom_up (p = 0.00)

  • mutate has 1.01 ± 0.00 times more coverage than top_down (p = 0.00)

5 Minutes of Fuzzing
  • bottom_up has 1.01 ± 0.01 times more coverage than arb (p = 0.04)

  • mutate has 1.47 ± 0.02 times more coverage than arb (p = 0.00)

  • top_down has 1.06 ± 0.02 times more coverage than arb (p = 0.00)

  • mutate has 1.45 ± 0.01 times more coverage than bottom_up (p = 0.00)

  • top_down has 1.05 ± 0.02 times more coverage than bottom_up (p = 0.00)

  • mutate has 1.38 ± 0.02 times more coverage than top_down (p = 0.00)

Conclusion

The mutate fuzzer performs best. It vastly outperforms all the others at 5 minutes of fuzzing (36-49% more coverage), and while the rest narrow that gap after 24 hours of fuzzing, mutate maintains its lead (1-2% more coverage).

The comparison between arb and mutate is as apples-to-apples of a comparison as it gets between idiomatic test-case generation and mutation in Rust: derive(Arbitrary) and derive(Mutate). They use the same fixup method to ensure that the resulting Wasm instructions are valid. The fuzzer built with mutatis and test-case mutation provides better coverage over time than the fuzzer built with arbitrary and test-case generation. When writing structure-aware fuzzers, I used to reach for arbitrary; in the future, I will reach for mutatis instead.

The top_down fuzzer performs second-best, and is best of the generation-based fuzzers. This aligns with results from the rgfuzz paper, which found that top-down Wasm instruction generation resulted in better instruction diversity than bottom-up generation. This result is intuitive, they point out, because Wasm instructions tend to have more operands than results, which means that more candidates are filtered out from consideration when generating instructions in forward order from operands to results (bottom-up) than when generating them in backward order from results to operands (top-down).

Subjectively, none of the approaches feel significantly more-complicated nor easier to implement than the others. All approaches require a stack of types, representing the generated Wasm’s operand stack, at some point in their implementation. Some require it during instruction generation (top_down and bottom_up) while others require it during fixup (mutate and arb). Adding support for new Wasm instructions is roughly the same in all of them: add a new variant to enum Inst and define its operand and result types. top_down and bottom_up additionally require adding a line for the new instruction in their choose_inst_{top_down,bottom_up} functions, but this could be avoided with some targeted macro_rules! sugar.

The fixup method fixes instructions in a forwards order; as future work, it would be interesting to implement a backwards_fixup method that fixes instructions in a backwards order and see if mutate and backwards_fixup outperforms the current mutate and forwards fixup the same way that backwards generation (top_down) outperforms forwards generation (bottom_up).

fixup makes an attempt to reuse stack operands when it can, rather than synthesize dummy constants or drop already-computed values, but the attempt is somewhat half-hearted. Dropping operands introduces dead code, which is not very interesting for exercising deep into the compiler pipeline. Dummy constants are not that interesting either. Therefore, another potential line of follow-up work would be to investigate ways to maximize operand reuse and minimize drops and dummy constants inserted while ensuring validity. That could include storing values to memory or globals instead of droping them when possible. It could even include liberating ourselves from the stack-focused paradigm we’ve had thus far.

WebAssembly is a stack-based language and so it is natural that our approaches have focused on producing stack-y code. But, in practice, optimizing WebAssembly compilers like Wasmtime’s use a static single-assignment intermediate representation, and erase the operand stack early in their compilation pipelines. Therefore, from these compilers’ point of view, the following two WebAssembly snippets are identical:

;; `x = a + (b * c)` in a "stack-y" encoding and
;; without temporary locals.
local.get $a
local.get $b
local.get $c
i32.mul
i32.add
local.set $x

;; `x = a + (b * c)` in a "non-stack-y" encoding
;; that uses temporary locals for every operation.
;;
;; Equivalent of
;;
;;     temp0 = b * c
;;     temp1 = a + temp0
;;     x = temp1
local.get $b
local.get $c
i32.mul
local.set $temp0
local.get $a
local.get $temp0
i32.add
local.set $temp1
local.get $temp1
local.set $x

Producing code that uses many temporaries in this manner might be easier than code that doesn’t, but, more importantly, it may enable better reuse of already-computed subexpressions, emit less dead code, and ultimately produce more interesting data-flow graphs that better exercise the deep innards of the compiler.

A final vein of interesting follow-up work to mine would be comparing arbitrary-based generators and mutatis-based mutators for structured inputs that are not programming languages and when the SUT we are fuzzing is not a compiler. Do we see these same results when, for example, producing PNG images to fuzz an image-transformation library?

Here is the source code for this experiment, including the three generators, one mutator, raw benchmark data, and benchmarking harness. The README includes instructions on running the benchmarks yourself.


  1. WebAssembly’s stack-based instructions encode an expression tree — local.get $a; local.get $b; local.get $c; i32.add; i32.mul is isomorphic to a * (b + c) — so the experiment should be relevant and applicable to any other generator or mutator for a programming language with expressions, even if it might not appear so at first glance. 

  2. Ignoring its rule-guided bit, which is orthogonal and could be applied to bottom_up as well. 

  3. Structure-aware fuzzing and property-based testing are basically the same: convergent evolution from different communities. 

Andreas Farre

Session History Diagrams in Firefox DevTools

I’ve spent a lot of time at Mozilla working on session history, the machinery that keeps track of where you’ve been so the back and forward buttons do something sensible. It’s one of those parts of the browser that sounds simple from the outside and turns out to be anything but. Once you add iframes, nested iframes, and the subtle rules about when a navigation creates a new entry versus replacing the current one, the state you’re reasoning about gets large and hard to hold in your head.

For years my main tool for understanding that state was reading code and printing things to a log. That works, but it’s slow, and it never quite shows you the shape of the thing. So I built a way to see it: a new DevTools panel in Firefox Nightly called Session History Diagrams.

Enabling it

The panel is available in Firefox today, behind a pref. It’s been there in some form since Firefox 150, growing more stable with each release. To turn it on, set devtools.application.sessionHistory.enabled to true in about:config, then reload DevTools. The new panel lives under the Application tab, next to Service Workers and Manifest, and it draws the browser’s session history as a diagram that updates as you navigate.

Since Firefox 153 it also works over remote debugging. Connect to a device from about:debugging and you can watch the session history of a page running on Android, the same as you would on the desktop.

Jake diagrams

I didn’t invent the idea of drawing this. The HTML spec already has a notation for it, called a Jake diagram after Jake Archibald, and that’s where I started. It’s a tabular notation where columns represent steps in session history, and rows represent navigables (the top-level browsing context plus any iframes). Background colors identify documents, a fresh color marking a new document loaded in that navigable, and the current step is shown in bold. It’s a genuinely useful way to capture multi-navigable interactions that are otherwise hard to describe in prose.

These diagrams don’t have to be drawn by hand. Domenic Denicola, one of the HTML spec editors, built a Jake diagram generator that turns a description of a navigation sequence into a rendered diagram. That’s where I first started playing with a more dynamic approach to the visualization. The thing I missed the most was being able to build a history up step by step rather than describe a finished sequence all at once. So I wrote rejake, a small tool that draws diagrams in the same style1, but lets you construct the history one step at a time.

But rejake, like the spec’s diagrams and Domenic’s generator before it, was stuck with a limitation the spec itself admits to, that they only work with a single level of nesting. That was exactly my problem. Real pages nest iframes inside iframes, and the bugs I was chasing usually lived down in that deeper nesting, precisely where the diagram stops being able to help. And however I drew them, I was still typing the history out by hand. It’s a short step from there to wanting the diagram to draw itself from the browser’s actual session history instead2.

Firefox Session History Diagrams

So the panel extends Jake diagrams to handle arbitrary nesting. Every column is a step in the session history. Every row is a frame, listed in pre-order from the frame tree: top-level document first, then its first iframe, then that iframe’s children, and so on. The current entry is highlighted in blue, and the diagram updates live as you navigate.

The recording above is an ordinary bit of browsing, a handful of pages visited one after another. The top row tracks the page you’re actually looking at, and the current position is the one in blue. The interesting part is everything underneath that top row.

Some of those pages didn’t just load a single document. They pulled in nested frames of their own, and the diagram stacks those below the page that owns them. None of that is visible in the address bar or anywhere in the page chrome, and the frames come and go as you move through the history. Normally you’d have no way of knowing they were ever there. Here you can read straight off the diagram which frames a given step carried, when each one entered, and when it dropped away again.

Who else might want this

I built this for myself, working on Gecko’s session history internals, where being able to watch the diagram change while reproducing a bug turns opaque state into something I can point at. But it turns out I’m not the only one who hits this wall. Plenty of people working elsewhere in Gecko, anywhere near navigation, end up reasoning about the same state, and now we all share one picture of it.

If you build single-page applications, or work with the History API or Navigation API, you’ve probably run into the same kind of confusion from the other side. A push where you expected a replace, a missing history entry, an iframe that accumulated entries unexpectedly. These are hard to reason about without seeing the state directly, and that’s exactly what the diagram gives you.

Session history isn’t a Firefox-specific problem either. Every engine implements the same part of the HTML spec, and Jake diagrams come from that shared spec. The panel only ever shows Firefox’s state, but the rules are the same everywhere, so if you work on another engine it can still be a useful reference for how one implementation behaves. It’s often the only practical way to surface an interoperability difference, which might be a bug in any of the engines, but stays hidden until you can actually see it.

Thanks

A big thanks to Nicolas Chevobbe, whose assistance was invaluable in getting the DevTools integration right. The work, including what’s still to come, is tracked in Bug 2015726. There’s a fair bit still on that list, like marking whether a step was a push or a replace, surfacing back/forward cache state, tying the diagram into the Network and Inspector panels, and more, all heading toward fuller DevTools support for Navigation and Session History.

Notes
  1. Which, naturally, meant re-implementing the whole of Session History along the way. 

  2. Getting nerd-sniped by Jan Jaeschke definitely contributed as well. 

Olivier Mehani

Optional Docker services and dependencies

Like many, docker and and compose have become my go-to tool to create software that can be conveniently deployed to production with a limited amount of headache. However, many tasks, and sometimes whole services, pertain only to the development side of the workflow, and need to stay there.

Moreover, some tasks, such as time-consuming provisioning tasks, are only on-demand one-offs. They shouldn’t run at all most of the time, but they should slot into the dependency graph correctly when needed.

tl;dr: I realised that docker compose supports profiles, which allows services to be enabled conditionally, along with the depends_on.[].required option, to ignore them when they are disabled. Profiles are also useful to package actions and triggers to run on demand, so they are not started by default.

We can start with a simple setup where our long-running main service depends on an init service to perform preliminary steps. This can be setup with depends_on the compose.yaml.

services:
  main:
    image: debian:latest
    command: "sh -c 'while : ; do echo main; sleep 10; done"
    depends_on:
      init:
        condition: service_completed_successfully

  init:
    image: debian:latest
    command: sh -c 'echo init; sleep 10'

Even when run ning the main container, we get the right dependency (and delay). So far so good (though up will show the output from all containers.

But what if we have another, much more time consuming, initialisation step?

services:
   [...]
   opt-init:
    image: debian:latest
    command: sh -c 'echo opt-init; sleep 100'

Perhaps we are lucky, and while it needs to run once, we don’t need it to run everytime (think: database setup).

Docker compose can use profiles to select when services are started. It will then only be started when this profile is selected. Services without explicit profile will always be started, but any service with one or more profile listed will only get started iff that profile is selected.

We can make the opt-init service part of the opt profile. We can also make the main service dependent on it, so it is started beforehand.

services:
  main:
    [...]
    depends_on:
      [...]
      opt-init:
        condition: service_completed_successfully

  opt-init:
    [...]
    profiles:
      - opt

This works well enough when the opt profile is specified but… Oh no! If the profile is not specified, the dependency on the opt-init isn’t resolvable, and none of the stack can spin up with just docker compose up

Fortunately, this is easily solved with the required attribute of the depends_on objects.

services:
  main:
    [...]
      opt-init:
        condition: service_completed_successfully
        required: false

And that’s really all there is to it: with the right profile, the optional dependency is started in the desired order, but its absence is otherwise transparently ignored. Both docker compose up and docker compose --profile opt work as desired.

Profiles afford us another useful trick: on-demand tasks not started by default. This can be handy for maintenance tasks (data cleanup, garbage collection, …) or test scripts (running test workload, sending message, …). Those are handy during development, but would not be necessary, or take a different form, in other deployments.

services:
  [...]
  say-hello:
    image: debian:latest
    profiles:
      - hello
    command: echo hello
    depends_on:
      main:
        condition: service_started

Conveniently, when explicitly running a service, it is not necessary to request a matching profile, keeping the command line lean: docker compose run say-hello.

So here we are. Compose profiles allow us to control which services get started, and mark some as conditional. This, coupled with the ability to mark some depends_on rules as not required is a good way to seamlessly prevent heavy or otherwise time consuming services from starting when not needed, while retaining proper dependency ordering when enabled.

For completeness, the full, final, compose.yaml looks as follow.

services:
  main:
    image: debian:latest
    command: "sh -c 'while : ; do echo main; sleep 10; done'"
    depends_on:
      init:
        condition: service_completed_successfully
      opt-init:
        condition: service_completed_successfully
        required: false

  init:
    image: debian:latest
    command: sh -c 'echo init; sleep 10'

  opt-init:
    image: debian:latest
    profiles:
      - opt
    command: sh -c 'echo opt-init; sleep 100'

  say-hello:
    image: debian:latest
    profiles:
      - hello
    command: echo hello
    depends_on:
      main:
        condition: service_started

The post Optional Docker services and dependencies first appeared on Narf.

The Servo Blog

April in Servo: new Android UI, focus, forms, security fixes, and more!

Servo 0.2.0 contains all of the changes we landed in April, which came out to yet another record 534 commits (March: 530). For security fixes, see § Security.

We’ve shipped several new web platform features:

Plus a bunch of new DOM APIs:

  • ‘selectionchange’ events on <input> and <textarea> (@TimvdLippe, #44461)
  • Storage­Manager, in experimental mode (@Taym95, #43976)
  • active­Element on Document and Shadow­Root (@mrobinson, #43861)
  • crypto.subtle.supports() (@kkoyung, #43703) – Servo is the first major browser engine to support this!
  • cell­Padding, cell­Spacing, and align properties on HTML­Table­Element (@mrobinson, #43903) – previously supported in HTML only
  • related­Target on ‘focus’ and ‘blur’ events (@mrobinson, #43926)
  • transfer­From­Image­Bitmap() on Image­Bitmap­Rendering­Context (@Messi002, #43984)

Servo’s support for text in Chinese, Japanese, and Korean languages has improved, with correct wrapping in the layout engine (@SharanRP, #43744), and CJK fonts now enabled in servoshell’s browser UI on Windows, Linux, and FreeBSD (@yezhizhen, @CynthiaOketch, @nortti0, #44055, #44138, #44514).

Navigating to a JSON file as the top-level document now renders the JSON with an interactive pretty-printer (@webbeef, @TimvdLippe, #43702).

April was a big milestone for Servo, with some automated tests failing because they had hard-coded cookie expiry dates set to April 2016 plus ten years. Surprise! We’re still here. Here’s to the next 100 years of Servo (@jdm, #44341).

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

Security

Crypto­Key now zeroes buffers containing key material after use (@kkoyung, #44597).

With only a few exceptions, you can only access DOM APIs in another document if that document is in the same origin. But if that document is in the same site with a different port number, Servo currently allows these accesses even though it shouldn’t. We’ve fixed some (but not all) of these incorrect accesses, specifically those that involve binding a Window or Location method in this document with a this from the other document (@yvt, @jdm, #28583).

We’ve fixed a bug where local­Storage and session­Storage were usable in sandboxed <iframe> and shared with every other sandboxed <iframe>, rather than throwing Security­Error (@Taym95, #44002).

We’ve fixed a bug where local­Storage and session­Storage were shared between all <iframe srcdoc> documents, rather than isolated using the origin of the containing document (@niyabits, #43988, #44038).

We’ve fixed a bug where IndexedDB was usable in sandboxed <iframe> and data: URL web workers (@Taym95, #44088).

We’ve fixed a bug where pages in some IP address origins can evict cookies from other IP address origins (@officialasishkumar, #44152). Only evicting cookies was possible, not reading or writing them.

We’ve fixed an out-of-bounds memory read in tex­Image3D() on Web­GL2­Rendering­Context (@simartin, #44270), and fixed some undefined behaviour in servoshell’s signal handler (@Narfinger, #43891).

Work in progress

IndexedDB is now enabled in servoshell’s experimental mode (@arihant2math, #44245). As always, embedders can enable it with Preferences::dom­_indexeddb­_enabled (@arihant2math, #44245, #44283).

IndexedDB now uses Servo’s new “client storage” system, which is based on the Storage Standard and will allow us to have a unified on-disk format and quota management for all web platform features that persistently store data (@gterzian, #44374, #43900). We’ve also made key range queries more efficient (@arihant2math, #39009), landed improvements to IDB­Database, IDB­Object­Store, IDB­Cursor, IDB­Key­Range, IDB­Request, and to the handling of transactions, keys, values, and exceptions (@Taym95, #44128, #43901, #44009, #43914, #44161, #44183, #44059, #44215, #42998, #43805).

We’ve made more progress on the Intersection­Observer API, under --pref dom­_intersection­_observer­_enabled (@stevennovaryo, @jdm, #42204).

We’re continuing to implement document.exec­Command() for rich text editing (@TimvdLippe, #44529), under --pref dom­_exec­_command­_enabled. This release adds support for the ‘bold’, ‘font­Name’, ‘font­Size’, ‘italic’, ‘strikethrough’, and ‘underline’ commands (@TimvdLippe, @jdm, @mrobinson, #44511, #43287, #44432, #44410, #44194, #44030, #44039, #44041, #44075, #44234, #44250, #44331, #44390, #44137, #44293, #44312, #44347).

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

Servo can now build a very basic accessibility tree for web contents, under --pref accessibility­_enabled (@alice, @delan, @lukewarlow, #42338, #43558, #44437, #44438). This includes text runs, plus nine other non-interactive accessibility roles (@alice, @delan, #44255). We’ve also fixed a crash when reloading pages with accessibility enabled (@alice, #44473), and made accessibility tree updates more efficient (@alice, #44208).

We’ve started implementing the Sanitizer API, under --pref dom­_sanitizer­_enabled (@kkoyung, #44198, #44290, #44335, #44421, #44452, #44481, #44585, #44594).

We’ve also started implementing Shared­Worker, under --pref dom­_sharedworker­_enabled (@Taym95, #44375, #44440).

We’re working on the Wake­Lock API too, under --pref dom­_wakelock­_enabled (@TG199, @rovertrack, #43617, #44343).

servoshell

servoshell for Android now has a revamped browser UI, including a new history view (@espy, #43795), the apk is 30% smaller (@jschwe, #44278, #44182), and we’ve fixed the black screen bug when closing settings or switching back from another app (@yezhizhen, #44327). You can now close tabs on OpenHarmony too (@Narfinger, #42713).

As for servoshell on desktop platforms, we’ve fixed some focus- and IME-related bugs (@mrobinson, #43872, #43932), and on Windows, we now install a normal shortcut without the strange behaviour of an “advertised” shortcut (@yezhizhen, #44223).

For developers

When using the Inspector tab in the Firefox DevTools, the Rules panel now includes declarations in ‘@layer’ rules (@arabson99, #43912).

When logging expressions in the Console tab, and when hovering over symbols in the Debugger tab, you can now get more information about the contents of functions, arrays, objects, and other values (@atbrakhi, @eerii, #44172, #44173, #44022, #44233, #44196, #44181, #44064, #44023, #44164, #44369, #44262).

When using the Debugger tab, you can now use the Scopes panel to inspect local and global variables (@eerii, @atbrakhi, #43792, #43791), you can now debug web worker scripts (@atbrakhi, #43981), and we’ve started implementing blackboxing, aka the Ignore source button (@freyacodes, #44142).

We’ve also landed some initial support for the Style Editor tab (@rovertrack, #44517, #44462).

We’re working towards re-enabling our automated DevTools tests in CI, which should make the feature more reliable (@freyacodes, #44577), and we’ve landed a small build reproducibility fix too (@jschwe, #44459).

For developers of Servo itself, please note that the Cargo ‘release’ profile is no longer #[cfg(debug­_assertions)] (@jschwe, @mrobinson, #44177). If you’ve been using ‘release’ as a “faster ‘debug’ with assertions” build locally, consider switching to ‘checked-release’ or ‘medium’.

The pull request template has been updated (@mrobinson, #44135). ‘Testing’ and ‘Fixes’ should go at the bottom of the PR description, and ‘Testing’ is about automated tests, not how you tested the PR locally.

We’ve made more progress on the new dev container, which will provide an alternative to our usual procedures for setting up a Servo build environment (@jschwe, @sagudev, #44126, #44111, #44162, #44641, #44109). Keep an eye out for that in the book!

In the meantime, did you know that you can use Lix or Nix to build Servo on Linux with a lot less hassle, even if you’re not using NixOS? For now at least, head to the NixOS page in the book to learn more. We’ve also fixed a regression that made --debug-mozjs and MOZJS­_FROM­_SOURCE builds take much longer to complete on Linux when not using Nix (@jschwe, #44346).

We’ve fixed building Servo with the ‘jitspew’ feature in mozjs, allowing you to set IONFLAGS to enable JIT logging (@simonwuelker, #44010). We’ve also fixed build issues on Windows and FreeBSD (@zhangxichang, @mrobinson, #44264, #44591).

Embedding API

With this second monthly release of the Servo library, we have some quick notes about API stability and semver compatibility:

  • The ‘servo’ package follows Cargo’s rules for semver compatibility. 0.1.1 is compatible with version 0.1.0, but 0.2.0 is a breaking update.

  • Until we integrate semver analysis into our release process, each monthly release will have a breaking version number, while non-breaking version numbers may be used for LTS updates.

  • In general, dependencies of ‘servo’, like ‘servo-base’ and ‘servo-script’, do not use semver. Any release may include breaking changes.

We’ve fixed a build failure affecting embedders with a new or updated Cargo.lock (@jschwe, #44093), and landed several other changes to help us with the Servo library release process (@jschwe, @mukilan, #43972, #44642, #43182, #43866, #44086, #43797).

Breaking changes:

You can now load a URL with custom request headers by calling Web­View::load­_request (@Narfinger, @longvatrong111, @mrobinson, #43338).

You can now retrieve cookies asynchronously by calling Site­Data­Manager::cookies­_for­_url­_async (@longvatrong111, #43794).

The synchronous version of that method, Site­Data­Manager::cookies­_for­_url, was previously not callable because Cookie­Source was not exposed to the public API, but we’ve fixed that now (@TG199, #44124).

You can now clear session cookies without clearing permanent cookies by calling Site­Data­Manager::clear­_session­_cookies (@longvatrong111, #44166).

When intercepting requests with Servo­Delegate:: and Web­View­Delegate::load­_web­_resource, we now include a destination and referrer­_url in the Web­Resource­Request, which can be helpful if you’re implementing ad blocking (@webbeef, #44493).

You can configure Servo to write all of its storage to a unique directory for that session by enabling Opts::temporary­_storage (@janvarga, #44433). Note that these unique directories currently persist after Servo exits, so it’s an isolation feature, not a privacy feature.

Window­Rendering­Context::new and Software­Rendering­Context::new now return an error if the given size is less than 1x1 (@freyacodes, @mrobinson, #44011).

We’ve improved our API docs for Web­View, Web­View­Builder, Web­View­Delegate, ServoDelegate, Prompt­Dialog, Web­Resource­Load, Web­Xr­Registry, Preferences, and servoshell’s EXPERIMENTAL­_PREFS (@simonwuelker, @TG199, @sabbCodes, @jdm, @rovertrack, #43892, #43787, #44171, #43947).

We’ve also improved our API docs for Opts, Output­Options, Diagnostics­Logging, Pref­Value, servo::opts, and servo­_config (@mukilan, #43802).

More on the web platform

Tab navigation now works across <iframe> boundaries (@mrobinson, #44397), and Ctrl+Backspace (or ) now deletes a whole word in input fields (@mrobinson, #43940).

Tab characters are now rendered correctly in <pre> (and other elements with ‘white-space: pre’), with proper tab stops (@mrobinson, @SimonSapin, #44480). Spaces are now rendered correctly in 2D <canvas>, instead of twice as wide as they should be (@mrobinson, #43899).

<a href> now correctly resolves the URL with the page encoding (@sabbCodes, #43822).

We’ve improved the default appearance of <input type=file> (@sabbCodes, #44496) and <textarea placeholder> (@mrobinson, #43770).

All keyboard events, mouse events, wheel events, and pointer events, other than ‘pointerenter’ and ‘pointerleave’, now bubble out of shadow roots (@simonwuelker, @webbeef, #43799, #44094). ‘error’ events on Window now report the correct filename (source in onerror) and lineno (@Gae24, #43632).

console.log() and friends now support printf-style formatting directives, although for now %c is ignored (@TG199, #43897).

file: URLs are now considered secure contexts, so they can now use features like crypto.subtle and crypto.random­UUID (@simonwuelker, #43989).

Exception messages have improved in Location, Static­Range, and the HTML­Element family of types (@arihant2math, @MuhammadMouostafa, @treetmitterglad, #44282, #43260, #43882).

We’ve improved the conformance of fetch algorithms (@yezhizhen, #43970, #43798), focus and tab navigation (@mrobinson, #43842, #44029, #44360, #43859, #44535), form submission (@TG199, #43700), JS modules (@elomscansio, @Gae24, #43741, #44179, #44042), page navigation (@TimvdLippe, #43857), <svg view­Box> (@yezhizhen, #44420), ‘attr()’ (@Loirooriol, #43878), ‘:focus’ (@mrobinson, #43873), ‘font’ (@RichardTjokroutomo, #44061), ‘@keyframes’ (@simonwuelker, #43461), ‘@property’ (@Loirooriol, #43878), ‘load’ events (@jdm, @arabson99, #43807, #44046), fetch­Later() (@TimvdLippe, #43627), axes and buttons on Gamepad (@log101, @rovertrack, #44411, #44357), copy­Tex­Image­2D() on Web­GL­Rendering­Context (@simartin, @mrobinson, #43608), tex­Image3D() on Web­GL2­Rendering­Context (@simartin, #44367), environment­Blend­Mode on XR­Session (@msub2, #44155), mark() and measure() on Performance (@shubhamg13, @simonwuelker, #44471, #44199, #43990, #43753), and Performance­Resource­Timing (@shubhamg13, #44228).

We’ve fixed bugs related to console logging (@sabbCodes, #44243), ‘animation’ (@mrobinson, #44299), ‘box-shadow’ (@yezhizhen, #44474, #44457), ‘display: contents’ (@Loirooriol, @mrobinson, #44551, #44299), ‘display: inline-flex’ (@SimonSapin, #44281), ‘display: table-cell’ (@Loirooriol, #44550), ‘display: table-row-group’ (@Veercodeprog, #43674), ‘overflow-x: clip’ and ‘overflow-y: clip’ (@Messi002, #43620), ‘position: absolute’ on grid items (@nicoburns, #44324), ‘word-spacing: <percentage>’ (@sabbCodes, #44031), remove­Child() on Document (@rovertrack, #44133), and URL.revoke­Object­URL() (@simonwuelker, @jdm, #43746, #43977, #44035).

Performance and stability

We’ve fixed some big inefficiencies in Servo. append­Child() with nested shadow roots is no longer O(2n) (@yezhizhen, @webbeef, #44016), and we’ve halved the time it takes to load the ECMAScript spec by fixing the O(whole DOM tree) processing of ‘id’ and ‘name’ attributes (@simonwuelker, #44120, #44127, #44117).

Servo makes its first TLS connection in each session 30–60 ms faster (@jschwe, #44242), and we’ve instrumented the Servo and servoshell startup processes to find more opportunities for optimisation (@jschwe, #44443, #44456).

Like most browser engines, Servo is a multi-threaded (and sometimes multi-process) system requiring a great deal of IPC messages to keep everything connected. Two key components of this system are the constellation thread, which manages the engine as a whole, and the script threads (or web processes), which render the web pages. Sending these messages can be expensive though, so to reduce unnecessary IPC traffic, we’ve landed an optimisation that allows script threads to selectively receive only the relevant messages from the constellation (@webbeef, #43124).

We’ve reduced the memory usage of each Attr, Text, and Character­Data node in the DOM by 16 bytes (@mrobinson, @Loirooriol, #44074), and fixed a memory leak when deleting <video controls> or <audio controls> (@Messi002, #43983).

Our about:memory page is more accurate now too, with new tracking of libc memory allocations on macOS, improved tracking of libc memory allocations on Linux (@jschwe, #44037), and more accurate tracking of Path­Buf and types in tokio, http, data­_url, and urlpattern (@Narfinger, #43858).

Less memory usage isn’t always better in browser engines though, because there are many kinds of caches and other optimisations we can do to make browsing the web faster, at the expense of increased memory usage. For example, we can greatly speed up prototype checks for DOM objects by storing a number in each object that identifies the concrete type, at the expense of making each DOM object 64 bits larger (@webbeef, #44364).

Layout can now reuse fragments in later reflows, in many cases that involve block layout or ‘position: absolute’ (@mrobinson, @lukewarlow, @Loirooriol, #42904, #44231). We’re also working on reusing shaping results in later reflows, and making inline layout more efficient (@mrobinson, #44370, #43974, #44436).

We’ve landed several changes that should reduce the binary size of Servo (@rovertrack, @mrobinson, @nicoburns, @Narfinger, #44227, #44221, #44303, #44338, #44428, #44134).

We’ve also reduced clones, allocations, borrow checks, GC rooting steps, and other operations in many parts of Servo (@rovertrack, @Narfinger, @Loirooriol, @yezhizhen, @simonwuelker, #44008, #44544, #44271, #44279, #43826, #44052, #44139).

Several crashes have been fixed:

We fixed a crash in servoshell when pressing keys like Ctrl+2 or ⌘2 with not enough tabs open (@mrobinson, #44070).

DOM data structures (#[dom­_struct]) can refer to one another, with the help of garbage collection. But when DOM objects are being destroyed, those references can become invalid for a brief moment, depending on the order the GC finalizers run in. This can be unsound if those references are accessed, which is a very easy mistake to make if the type has an impl Drop. To help prevent that class of bug, we’re reworking our DOM types so that none of them have #[dom­_struct] and impl Drop at the same time (@willypuzzle, #44119, #44501, #44513).

We’ve improved our static analysis for GC rooting (@officialasishkumar, #44489), and we’ve continued our long-running effort to use the Rust type system to make certain kinds of dynamic borrow failures impossible (@sagudev, @TimvdLippe, @Narfinger, @elomscansio, @Gae24, @rovertrack, @yezhizhen, @nodelpit, #43174, #43524, #43928, #43943, #43942, #43944, #43946, #43952, #43975, #44018, #44175, #44241, #44368, #44406, #44441, #44422, #44475, #44478, #44484, #44476, #44490, #44477, #44494, #44497, #44498, #44495, #44505, #44506, #44507, #44508, #44509, #44510, #44512, #44482, #44527, #44528, #44531, #44534, #44542, #44533, #44543, #44553, #44547, #44563, #44562, #44565, #44558, #44583, #44606, #44605, #44608, #44602, #44584, #44620, #44590, #44254, #44628, #44629, #44638, #44626, #44081).

Thanks to a wide range of people, we’ve also landed a bunch of cleanups and refactors (@delan, @alice, @Skgland, @atbrakhi, @eerii, @sabbCodes, @jdm, @thebabalola, @CynthiaOketch, @kkoyung, @TimvdLippe, @rovertrack, @webbeef, @arabson99, @yezhizhen, @simonwuelker, @mrobinson, @nicoburns, @longvatrong111, @niyabits, @treetmitterglad, @foresterre, @mukilan, @elomscansio, @freyacodes, @StaySafe020, @TG199, #43772, #44006, #43860, #44121, #44160, #43884, #44154, #44569, #43939, #44003, #44110, #44122, #43824, #44635, #44103, #43978, #44092, #44114, #44277, #44454, #44274, #44237, #44232, #44167, #44214, #43820, #43825, #43810, #43838, #43841, #43847, #43875, #43876, #43889, #43893, #43896, #43881, #43906, #43913, #43908, #43917, #43910, #43921, #43924, #43925, #43907, #43923, #43916, #43909, #43911, #43957, #43969, #43967, #43915, #43954, #43963, #43959, #43955, #44067, #44068, #44071, #44084, #44265, #44115, #44358, #43848).

Donations

Thanks again for your generous support! We are now receiving 7349 USD/month (+2.5% from March) 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 33 GitHub users (−4 from March) 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.

7349 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.

Frederik Braun

The S in interoperability

This is a blog post about standards, their proliferation and the issues that may arise. My first involvement with standards was just as a reader. To better understand complicated code or unexpected behavior in a protocol. After a while, I also got involved and helped clarify certain things to ensure implementations align on the same behavior in edge cases. Eventually, I found myself co-editing a specification - Subresource Integrity (SRI) which was published as a W3C Recommendation in 2015. The core idea behind SRI is that you include third-party JavaScript combined with a SHA2 digest of the expected file. If the browser does not find the downloaded URL to match the expected digest, the script will not execute. This allows using a fast CDN for JavaScript without giving them full control over the scripts on your page - essentially reducing the security risks.

The standard format for these digests is e.g., sha(size)-(base64 encoding of the digest). While computing the hash digest is rather straightforward, base64 comes in two encoding alphabets: First, a-zA-Z0-9/+ and secondly the url-safe variant which uses a-zA-z0-9_-. The specification examples all used the former.

Only approximately ten years after publication, in 2025, we still found a bug. As part of a compatibility report against Firefox not properly supporting a website, we found that the core issue was actually with a different browser. The other browser liberally accepted both types of encoding, which resulted in websites expecting support for base64 and base64url interchangeably. The page did not work in Firefox, because it did not accept all hashes a website wanted the browser to check, revealing a minor security issue.

The real fix would have been that the standard clarifies that the base64url variant is incorrect and the other browser engine changes their behavior.

But due to (somewhat unrelated) issues around proliferation of standards, web compatibility and the unfortunate market dominance of certain browsers, we went the other road. To support existing web content, we changed the standard to acknowledging that both types of encoding are considered valid representations.

This example shows, that it can take multiple years for subtle differences to appear. Interoperable specifications can establish a shared understanding along a "happy path", but not necessarily in adversarial settings. In addition, standards need to continuous maintenance and active stakeholders who ensure that implementations remain interoperable and secure over time.

From specification to standard

Originally, a specification is at first just a write-up, an idea how something could be better: How it should behave, how it works, what the data structures, the algorithms and the interactions of them look like. Anyone can come up with a grammar, a parser and a resulting data structure.

For a standard, this specification needs a shared agreement that is also widely and consistently implemented. This will work best with iterative co-design of the spec, the implementations and intense discussions of corner cases. Some may go further and use shared test suites.

This will lead to Interoperability (interop), but still requires constant maintenance and observation of the ecosystem beyond individual implementations. While interop is asymptotic and requires a shared agreement over time, security demands understanding - a broader reach that requires the inspection of limitations and subtle boundaries.

This deeper level of understanding is often missing when implementations consider syntax "simple enough" without reading the spec. The base64 SRI example is just one example, but there are more:

Many people have written their own parsers for text-based languages. You may have seen code that parses HTML with regular expressions. Other great examples of "easily" parsed languages are maybe XML, JSON, or YAML.

But these implementations often make different assumptions, leading to subtle incompatibilities or even security flaws.

Parser Differentials

More practical, let's look at an issue with JSON, to demonstrate the impact of handling input that is ostensibly simple. Let's examine this JSON string and the resulting data structure:

{
"test":0,
"test":1
}

When parsed into an object obj, what do you think will obj.test return? Most JSON parsers are so liberal that they will happily consume two dictionary keys with the same name "test". One implementation may simply assign obj.test twice: First with 0 and then overwrite it with 1. Another one might check for existing keys and reject the second "test" key silently, keeping the first one.

The lack of rigor in the original description of JSON as a "subset of JavaScript" was already acknowledged and raised as problematic in the JSON RFC (which came much later in 2017). But still to this day, many implementations allow input with duplicate dictionary keys and show divergent behavior.

While the examples with SRI and JSON are relatively harmless, real parser differential bugs were leading to code execution, authentication bypasses and more1.

What do we learn from this?

Perfect interoperability is not created through a specification, it needs constant maintenance. The ambiguity can only be removed through long-term commitment and regular feedback from implementations and users.

The same is true for security: The SRI bug persisted for ten years and nobody noticed how implementations disagreed and corner cases were overlooked. They only aligned due to a real, user-facing issue.

But these examples are not a warning sign, they are scar tissue that shows how the internet is made. Standards can only mature through vigilant maintenance.

The bug reports, the spec issues being filed, the shared test cases, sometimes even the random forum complaints. All of these help to remove ambiguity and allow internet standards to mature.

In the end, standards are not secure because they are written down. They are secure because people continue to question, understand, and maintain them.

The Rust Programming Language Blog

Announcing Rust 1.96.0

The Rust team is happy to announce a new version of Rust, 1.96.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.96.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.96.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.96.0 stable

New Range* types

Many users expect Range and related core::ops types to be Copy, but this is not the case: they implement Iterator directly, and it is a footgun to implement both Iterator and Copy on the same type so this has been avoided. RFC3550 proposed a set of replacement range types that implement IntoIterator rather than Iterator, meaning they can also be Copy. The standard library portion of that RFC is now stable, introducing:

  • core::range::Range
  • core::range::RangeFrom
  • core::range::RangeInclusive
  • Associated iterators

A Rust version in the near future will also add core::range::RangeFull and core::range::RangeTo as re-exports from core::ops (these do not implement Iterator and already implement Copy), and core::range::legacy::* as the new home for the current ranges. Range syntax like 0..1 still produces the legacy types for now, but will be updated to core::range types in a future edition.

With these stabilizations, it is now possible to store slice accessors in Copy types without splitting start and end:

use core::range::Range;

#[derive(Clone, Copy)]
pub struct Span(Range<usize>);

impl Span {
    pub fn of(self, s: &str) -> &str {
        &s[self.0]
    }
}

The new RangeInclusive also makes its fields public, unlike the legacy version which avoided exposing the exhausted iterator state. This isn't a concern with the new type since it must be converted to begin iteration.

Library authors should consider making use of impl RangeBounds in public API, which accepts both legacy and new range types. If a concrete type is needed, prefer using new ranges as this will eventually become the default.

Assert matching patterns

The new macros assert_matches! and debug_assert_matches! check that a value matches a given pattern, panicking with a Debug representation of the value otherwise. These are essentially the same as assert!(matches!(..)) and debug_assert!(matches!(..)), but the printed value improves the possibility of diagnosing the failure.

These new macros have not been added to the standard prelude, because they would collide with popular third-party crates that provide macros with the same name. Instead, they should be manually imported from core or std before use.

use core::assert_matches;

/// [Random Number](https://xkcd.com/221/)
fn get_random_number() -> u32 {
    // chosen by a fair dice roll.
    // guaranteed to be random.
    4
}

fn main() {
    assert_matches!(get_random_number(), 1..=6);
}

Changes to WebAssembly targets

WebAssembly targets no longer pass --allow-undefined to the linker which means that undefined symbols when linking are now a linker error instead of being converted to WebAssembly imports from the "env" module. This change prevents modules from linking unless all linking-related symbols are defined to catch bugs earlier and prevent accidental issues with symbol naming or similar.

Undefined linking-related symbols are often indicative of build-time related bugs or misconfiguration. If, however, the old behavior is intended then it can be re-enabled with RUSTFLAGS=-Clink-arg=--allow-undefined or by editing the source code and using #[link(wasm_import_module = "env")] on the block defining the symbol.

This change was previously announced on this blog, and now takes effect in Rust 1.96.

Stabilized APIs

Two Cargo advisories

Rust 1.96 contains fixes for two vulnerabilities for users of third-party registries.

  • CVE-2026-5223 is a medium severity vulnerability regarding extraction of crate tarballs with symlinks.

  • CVE-2026-5222 is a low severity vulnerability regarding authentication with normalized URLs.

Users of crates.io are not affected by either vulnerability.

Other changes

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

Contributors to 1.96.0

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

Firefox Tooling Announcements

New Deploy of PerfCompare (May 27th)

This Week In Rust

This Week in Rust 653

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
Project/Tooling Updates
Observations/Thoughts
Rust Walkthroughs
Miscellaneous

Crate of the Week

This week's crate is inline_tweak, a crate to embed tweakable constants inside your Rust application without full recompilation.

Thanks to Kill The Mule for the 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.

  • 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

352 pull requests were merged in the last week

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

This week was largely positive, with most of the improvements coming from algorithm change in visibility checking: #156228.

Triage done by @panstromek. Revision range: 281c97c3..783eb8c8

Summary:

(instructions:u) mean range count
Regressions ❌
(primary)
0.4% [0.1%, 0.7%] 5
Regressions ❌
(secondary)
0.5% [0.1%, 1.1%] 16
Improvements ✅
(primary)
-0.9% [-6.6%, -0.1%] 164
Improvements ✅
(secondary)
-0.4% [-1.3%, -0.1%] 51
All ❌✅ (primary) -0.9% [-6.6%, 0.7%] 169

2 Regressions, 2 Improvements, 5 Mixed; 2 of them in rollups 34 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
Compiler Team (MCPs only) Rust Rust RFCs Unsafe Code Guidelines

No Items entered Final Comment Period this week for Cargo, Language Team, Language Reference or Leadership Council. 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-05-27 - 2026-06-24 🦀

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

This overflows the trait solver today as well as my brain

Nadrieril on their blog

Thanks to Theemathas 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

Firefox Profiler Deployment (May 26, 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] Use @streamparser/json if the input is too large to fit in a V8 string (#6016)
  • [Nazım Can Altınova] Include --search option in pq filter push (#6026)
  • [fatadel] Translate URL track-index state through profile sanitization (#6000)
  • [Nazım Can Altınova] Print also the status output right after cli load command (#6019)

Other Changes:

  • [fatadel] Remove unused dependencies from package.json (#6010)
  • [Nazım Can Altınova] Make profiler-cli work in sandboxed environments (#6003)
  • [Markus Stange] Make profiler-edit run profile compacting before writing out the file (#6015)
  • [Markus Stange] Migrate from prettier to oxfmt (#5986)
  • [Markus Stange] Add a --symbolicate-wasm arg to profiler-edit. (#6008)
  • [Markus Stange] Build and upload the cli artifact in PRs (#6020)
  • [Nicolas Chevobbe] Update devtools-reps to 0.27.7 (#6030)
  • [Markus Stange/fatadel] Make withSize use a wrapper element so that it can stop calling findDOMNode (#5988)
  • [Markus Stange] Fix dhat importer (#6036)
  • [Nazım Can Altınova] Annotate inlined frames in CLI call trees and stacks (#6041)
  • [Nazım Can Altınova] Use proper types in cli tests instead of custom inline types (#6038)
  • [Nazım Can Altınova] Fix text truncation for frames named after Object.prototype methods (#6044)
  • [Nazım Can Altınova] Add missing key props to CodeErrorOverlay error list items (#6047)
  • [depfu[bot]] :up_arrow: Update oxfmt to version 0.51.0 (#6054)
  • [Nazım Can Altınova] :clockwise_vertical_arrows: Sync: l10n → main (May 26, 2026) (#6058)
  • [Nazım Can Altınova] Use URL-state symbol server for profiler-cli function annotate (#6051)
  • [Nazım Can Altınova] Bump profiler-cli version to 0.2.0 (#6059)

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

  • fr: YD
  • sr: Марко Костић (Marko Kostić)
  • tr: Ali Demirtaş
  • zh-CN: Olvcpr423
  • zh-CN: wxie

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

Andrew Halberstadt

Your New Job is Integrating Code

You felt it. The shift. That your role has fundamentally changed thanks to LLMs. It first entered your subconscious when you realized how easily you can now crank out PRs. You felt it more concretely (and less enthusiastically), as a reviewer when you opened your laptop one morning and noticed your review queue was double what it normally is thanks to everyone else cranking out PRs. And you feel this pervasive, general sense of friction.

It’s difficult to pinpoint exactly where this friction is coming from. Depending on the repository size and CI setup, it will be slightly different for everyone. It might involve longer review times or slipping review standards. You might be noticing more merge conflicts and merge related CI failures. Perhaps there are more failures sneaking through to main or CI is taking longer to give you results. You almost certainly feel the grind. People are on edge, tired; developers are pulling in opposite directions.

Here’s what LLMs shifted. The bottleneck is no longer producing code. The bottleneck is integrating it. The friction we’re feeling is a result of more PRs, more ideas, more reviews, more disagreements all made possible thanks to LLMs. In short, the problem can best be summarized by Figure 1:

But we’re living in a moment where many folks haven’t realized this yet, and are still under the impression that their job is to produce code.

It’s not. Your new job is to integrate it.

Mozilla Privacy Blog

Growing darkness: Against the rise of internet shutdowns

Disruptions to internet connectivity can occur in countless ways – from weather incidents, natural disasters and accidents to intentional interferences like cyberattacks and government-issued blackouts. Yet while some disruptions are unavoidable, deliberate shutdowns represent a fundamentally different and deeply concerning trend. They undermine the open, global nature of the internet and put the safety, security, and fundamental rights of millions at risk.

For over 25 years, Mozilla has worked to ensure that the internet remains a global public resource—open, accessible, and safe for all. This vision, grounded in the Mozilla Manifesto, holds that the internet must remain a shared, decentralized infrastructure that empowers individuals, supports civic participation, and enables economic opportunity. Internet shutdowns run counter to these principles by restricting access, concentrating control, and weakening the very foundations of the open web.

To help organizations study and document outages, Mozilla makes aggregated Firefox telemetry data available to help identify and understand connectivity disruptions. As 2026 progresses, this data continues to show significant outages affecting millions of people worldwide—many of them the result of deliberate restrictions.

As of late May, Iran’s internet blackout had been in place for over 80 days, making it the longest shutdown since the Arab Spring. Following an earlier shutdown amid nationwide protests in January 2026, Iranian authorities have restricted access to the internet since 28 February. This has meant that, for almost three months, millions of Iranians have been cut off from news, communication, work, education, and basic services. It also means that almost no independent information about the situation in Iran is leaving the country, making it almost impossible for humanitarian organizations to assess the situation on the ground. The shutdown has also had a massive impact on the Iranian economy, severely disrupting financial activity and blocking international transactions. Although Iran’s president has recently ordered an end to the shutdown, it is unclear how and when Iranians will be able to reconnect to the web.

When large numbers of Firefox users experience connection failures for any reason, this produces an anomaly in the recorded telemetry data. At the country or city level, this can provide a corroborative signal of whether an outage or intentional shutdown occurred. Our telemetry documents the magnitude of the latest outage in Iran. The graph below documents the effect of the outage in multiple ways, such as users’ country location, language and timezone.

Across the globe, governments are increasingly interfering with and limiting access to connectivity. Both the number of states limiting connectivity and the amount of internet shutdowns has been growing steadily. In 2025 alone, 313 shutdowns across 52 countries have been documented, a sad record. This is a stark indication that shutdowns and restrictions are no longer a rare emergency measure, but established levers of control.

While the triggers for shutdowns are varied, access to the internet continues to be blocked especially often in times of conflict and political unrest. Especially in the context of hostilities, political tensions or public health emergencies, access to connectivity is a basic humanitarian need.

Beyond their immediate human impact, blackouts also affect the internet itself. Local networks depend on each other to form the global internet, and local restrictions affect the resilience and reliability of the web at large. When governments deliberately disrupt connectivity, they do not only isolate populations; they also contribute to the fragmentation of the global internet, undermining trust, interoperability, and the stability of shared infrastructure. Over time, this erosion risks replacing a single, open web with a patchwork of disconnected or controlled networks.

Governments should foster the health of the internet, not erode it. Access to the internet is widely recognized as essential for enjoying human rights. It is an integral part of modern life, facilitating education, communication, collaboration, business and entertainment. Preserving the open web requires sustained commitment: resisting shutdowns, promoting transparency, and reinforcing the technical and governance frameworks that keep the internet global, interoperable, and accessible. The internet’s value—as a platform for opportunity, innovation, and human connection—depends on it remaining open to all.

The post Growing darkness: Against the rise of internet shutdowns appeared first on Open Policy & Advocacy.

Jonathan Almeida

Auto-resolve Jujutsu conflicts with your AI agent

With Jujutsu, I've been able to work in multiple workstreams more efficiently than before. This means that if I'm working on multiple things, there is a higher likelihood of something going stale while I wait for a review or touch multiple files. Dealing with conflicts aren't so bad these days, however if I can automate the easy ones, why not?

This is the prompt I've been using with my agent whenever I have a list of changes that have conflicts and don't need me to participate actively on it.

Using the jj version control system, fix the conflicts that are in the changesets from `<start_rev>` to `<end_rev>`. Keep trying until there are no more "(conflict)" in the changesets between those two IDs.

The Rust Programming Language Blog

Security Advisory for Cargo (CVE-2026-5223)

The Rust Security Response Team was notified that Cargo incorrectly handled symlinks inside of crate tarballs downloaded from third-party registries, allowing a malicious crate to override the source code of another crate from the same registry.

This vulnerability is tracked as CVE-2026-5223. The severity of the vulnerability is medium for users of third-party registries. Users of crates.io are not affected, as crates.io forbids uploading crates containing any symlink.

Overview

When building a crate, Cargo extracts its source code in a local cache (stored within ~/.cargo), reusing it for any future build. Cargo includes protections to prevent any file from being extracted outside of the crate's own cache directory.

It was discovered that it's possible to craft a malicious tarball able to extract files one level below the crate's own cache directory. With the way the cache is structured, that allowed the malicious crate to override the cache of other crates belonging to the same registry.

Mitigations

Rust 1.96.0, to be released on May 28th, 2026, will update Cargo to reject extracting any symlink within crate tarballs, regardless of whether they come from crates.io (which already forbids them) or third-party registries. Note that Cargo never added symlinks when running cargo package or cargo publish, so the impact of this should be minimal.

Users who are not able to upgrade to the most recent Rust version are recommended to audit the contents of their registry for the presence of any symlink, and to configure their registry to reject symlink (if such option is available).

Affected versions

All versions of Cargo shipped before Rust 1.96.0 are affected.

Acknowledgements

We'd like to thank Christos Papakonstantinou for reporting this to us according to the Rust security policy.

We also want to thank the members of the Rust project who helped us address the vulnerability: Josh Triplett for developing the fix; Arlo Siemsen for reviewing the fix; Emily Albini for writing this advisory; Emily Albini, Josh Stone and Manish Goregaokar for coordinating the disclosure; Ed Page and Eric Huss for advising during the disclosure.

The Rust Programming Language Blog

Security Advisory for Cargo (CVE-2026-5222)

The Rust Security Response Team was notified that Cargo incorrectly normalized the URLs of third-party registries using the sparse index protocol. If a hosting provider allowed multiple registries to be hosted with arbitrary names within the same domain, an attacker able to publish crates in a registry could obtain the credentials of others users of the same registry.

This vulnerability is tracked as CVE-2026-5222. The severity of the vulnerability is low, due to the extremely niche requirements needed to achieve the attack.

Overview

Originally Cargo only supported storing a registry's index within git repositories. Most git hosting solutions allow accessing a git repository with or without the .git suffix, so Cargo mirrored this behavior when normalizing registry URLs. This allowed credentials for https://example.com/index to be used for https://example.com/index.git.

This normalization was unintentionally applied to the new sparse indexes too. Sparse indexes can be hosted on any HTTPS server, which treat URLs ending with .git as different URLs than those without the suffix.

If the following conditions apply:

  • https://example.com/index is a sparse index.
  • https://example.com/index allows crates to depend on crates from any other registry.
  • The attacker is able to publish crates on https://example.com/index.
  • The attacker is able to upload arbitrary files to https://example.com/index.git.

...the attacker could configure https://example.com/index.git to be a Cargo sparse registry requiring authentication for downloads, and with a download URL pointing to a server recording any credentials set to it.

When the attacker then publishes a crate foo to https://example.com/index depending on a crate bar from https://example.com/index.git, and tricks the victim into downloading foo, Cargo will think the two registries share the same credential and send the victim's Cargo token to the malicious registry.

Mitigations

Rust 1.96, to be released on May 28th, 2026, will update Cargo to only strip the .git suffix from registry URLs using the git protocol. No mitigations are available for users of older versions of Cargo.

Affected versions

All versions of Cargo shipped between Rust 1.68 (the stabilization of sparse registries) and 1.96 are affected.

Acknowledgements

We'd like to thank Christos Papakonstantinou for reporting this to us according to the Rust security policy.

We also want to thank the members of the Rust project who helped us address the vulnerability: Arlo Siemens for developing the fix; Weihang Lo, Eric Huss and Emily Albini for reviewing the fix; Emily Albini for writing this advisory; Emily Albini, Josh Stone and Manish Goregaokar for coordinating the disclosure.

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

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.

The Mozilla Blog

Mozilla and Adafruit bring Web Serial workflows to Firefox

Launching Web Serial in Firefox 151

The web is built by communities, but not all communities use the web the same way. 

That philosophy shaped part of this week’s Firefox 151 release, which introduced support for the Web Serial API on desktop. Most folks won’t use this API, but for our community of builders and tinkerers, it unlocks the ability to use Firefox to communicate directly with compatible hardware devices like microcontrollers, development boards, and other serial-connected devices.

For developers, makers, educators, hardware enthusiasts, and embedded-device communities, browser-based hardware workflows have increasingly become part of the modern web experience. With Firefox’s browser engine, Gecko, now supporting Web Serial, users can now connect, code, configure, and control compatible hardware directly from the browser in many workflows, often without additional software or complicated setup. 

If you want to dive deeper into the technical details behind Web Serial support in Firefox 151, you can read our full engineering post here.

Adafruit collaboration

As part of this week’s launch, Adafruit, one of the internet’s most beloved open-source hardware communities, is collaborating with us to test and validate what browser-based hardware development can look like in Firefox with Web Serial support.

If you’ve ever spent time with CircuitPython, browser-based board programming, custom controllers, sensors, classroom kits, STEM homework assignments, or a desk covered in blinking microcontrollers—you probably already know Adafruit.

With Web Serial support in Firefox 151, Adafruit’s browser-based hardware workflows now work directly in Firefox as well, with no additional software or complicated setup required for many projects. We invite you to give it a try

Different communities care about different browser experiences. Some people want simplicity, familiarity and productivity. Others want flexibility, customization, and tools that support the way they work, build, experiment, and create. We want the web to be open, flexible, and shaped by the diversity of people building on it. 

If you’re wiring up your first board, experimenting with hardware projects, or dusting off an old electronics kit, give Adafruit and Web Serial in Firefox a try. 

Build something amazing. Make something useful. Tell us what works. Tell us what breaks. Most of all, make it your own.

Take control of your internet

Download Firefox

The post Mozilla and Adafruit bring Web Serial workflows to Firefox appeared first on The Mozilla Blog.

Hacks.Mozilla.Org

Announcing Web Serial Support in Firefox

Support for Web Serial in Firefox 151 for Desktop

Firefox can now connect directly to microcontrollers, development boards, 3D printers, power meters, and other serial-connected hardware from the web. Starting in Firefox 151 for Desktop, support for the Web Serial API allows web applications to communicate with compatible devices without requiring native software.

Web Serial compatible devices are popular among hobbyists, hardware hackers, educators, makers, and developers with use cases ranging from home automation to hardware prototyping and 3D printing. Web Serial support makes Firefox more useful for these kinds of projects.

One of the organizations that has demonstrated the value of Web Serial is Adafruit, a leader in open-source hardware and STEM education. They’ve made it quick and easy to install CircuitPython on their devices by delivering firmware over Web Serial. Then it’s straightforward to run Python programs on the device. Name your file code.py and, for most devices, the code can be installed by dragging-and-dropping the file onto the USB device. Your Python programs can interoperate with a web page over Web Serial using simple text-based I/O.

To install CircuitPython firmware with Firefox, we recommend using the Adafruit Web Serial Tool and not the OPEN INSTALLER method on the CircuitPython site.

Here’s an example using an Adafruit ESP32-S2 based board where messages sent from web code can be directly displayed on the device over Web Serial.

A video showing an example CircuitPython Web Serial project where a message is sent from a local web page using Web Serial to a device running a CircuitPython code.py file.

We’ve collaborated with Adafruit to test Firefox’s implementation against real hardware workflows commonly used by this community. The result: Firefox is a more practical browser for programming and interacting with hardware directly using web technology.

As an example of how you can combine Web Serial with electronics, Mozilla engineer Alex Franchuk created an amazingly fun and functional device that melds electronics and web editing. Check out the Page Playground.

The list of serial compatible devices includes Espressif ESP-based boards such as the popular ESP32 chips, Raspberry Pi Picos, 3D printers, LEGO devices, and many more. There are many tools for running your own code on these small affordable microcontroller boards, and with Web Serial it’s easier than ever to connect them to a computer and interact through a web-based user interface.

What is Web Serial?

Web Serial is a web API that allows a website to read and write to serial devices using JavaScript. See the MDN documentation for the details. While modern computers don’t typically include serial ports, serial devices connected to a USB port or paired via Bluetooth can advertise themselves as serial-capable devices so they appear as serial ports in the operating system.

The Web Serial API lets developers use the web platform to communicate with these devices. For example, websites can control devices or deliver firmware without requiring native applications or installers.

Mozilla’s own Florian Quèze, who has experimented with many projects to measure power consumption, demonstrated how Web Serial could be used to read power data from an off-the-shelf USB power meter and display it in Firefox. Florian’s code can also export the data into the Firefox Profiler, making it easy to visualize and share power data. Here’s the page and GitHub repo. The screenshots below show the page in action and the data in the Firefox Profiler after recording the power usage of a light with three brightness modes.

A screenshot of Florian’s site showing a power recording of a light with three brightness modes.The power meter used in the pictured power tests was the AVHzY C3 USB. The Joy-IT TC66C and YZXStudio USB ZY1280 were also successfully tested.

A screenshot of the Firefox Profiler displaying the imported power recording from Florian’s site.

Home Assistant is another example. It’s a popular (and growing) open source project for home automation. The ESPHome project offers Home Assistant-compatible firmware for affordable ESP32 and similar devices which can be installed and configured over Web Serial in just a few clicks.

Security and Privacy

There are clear security and privacy concerns with allowing the web platform to read and write to hardware devices. Most importantly, with Web Serial, websites do not have visibility or access to serial ports until the user explicitly allows it.

Ports are allowed on a per-site and per-port basis. The Web Serial API requires websites to call navigator.serial.requestPort(), which lets the user choose which port to allow access to, or disallow all access entirely. This means websites do not receive a list of connected devices and there is no useful fingerprinting information outside of the port the user selects.

A screenshot of the Web Serial port selection prompt in Firefox.

To help users understand when and why a site requests access to a serial port, Firefox uses add-on gating which we introduced with our Web MIDI API implementation. Compared to other web permission prompts, this gives the user a more detailed explanation of what they’re allowing. The add-on gating prompts appear before the port selection prompt the first time a site requests port access.

For organizations using Firefox Enterprise Policies, Web Serial is disabled by default. Administrators can explicitly allow or disallow Web Serial functionality across their organization using the DefaultSerialGuardSetting policy setting.

Standardization

While Web Serial still resides in the Web Incubator Community Group (WICG), we’re optimistic there’s a path to standardization given its scope and long-running incubation. We are pursuing standardizing the Web Serial API in the WHATWG in a new Workstream proposal and are excited to work with ecosystem partners and standards bodies to help shape access to peripherals on the web.

Feedback

If you already have a Web Serial workflow with a device you can test on, give Firefox a try. We’d love to hear what you’re building and which workflows matter most to you. Mozilla Connect is a great place to share projects, ask questions, and give feedback.

For technical issues, browse to support.mozilla.org or file a bug here.

The post Announcing Web Serial Support in Firefox appeared first on Mozilla Hacks - the Web developer blog.

The Mozilla Blog

Designing Firefox for the future

Crafted with care. Built for speed. Ready for what’s next.

A great browser is so intuitive that you often forget you’re using it. Yet today the internet is changing faster than ever, and your browser needs to keep up. Firefox is still the only browser built for people, not platforms: independent, customizable, private and firmly in users’ control. 

Keeping Firefox the best browser for being online today is what motivated our recent work to update Firefox’s design and design system. We’re aiming to deliver a more cohesive foundation for Firefox: making the browser feel cleaner, warmer, faster and more adaptable.

Internally, we’ve been calling this work Project Nova. The name fits: A nova can look like a new star, yet it comes from existing matter — a renewal, not a replacement. When it rolls out later this year, you can just call it Firefox. Here’s what it’s all about:

Privacy at the center

A good default matters. When you choose Firefox, privacy and clear data practices are there from the start. Our new design pulls privacy features forward, making it easier to find and use tools like our free, built-in VPN and private browsing.

We’re also redesigning Settings so choices about your data are easier to understand and act on. That includes controls for turning off AI features entirely, plainer language throughout, as well as tuning Enhanced Tracking Protection to match your preferred balance of protection and usability.

Speed you can feel

Privacy and speed aren’t trade-offs. When Firefox blocks trackers, pages load faster, too. We also prioritize the most important parts of a page before the optional stuff around the edges. In the last year, we’ve improved load times for key page content by 9%.

The new design can speed up your workflows, too. It’s easier to access tab groups, split view, and vertical tabs – putting these productivity features at your fingertips, but not in your face. 

And we’re bringing back compact mode. People told us that they missed it, and we listened. If you want your browser controls as condensed as possible, this one’s for you. 

Balancing the new and familiar

When it came to design, we wanted Firefox to feel current, but not generic. Warm, but still precise. More expressive, but never louder than the web itself. You’ll see the change first in the fundamentals: 

  • Tabs have a softer shape, with a subtle gradient that gives the active tab more presence and creates a sense of light around the browser chrome. 
  • Components are more rounded and consistent, so panels, menus, settings and browser controls feel like part of the same system. 
  • Icons have been updated to feel cleaner and more balanced across light and dark themes, supporting quick recognition without adding visual noise.
  • Spacing is rebalanced across the interface with the knowledge that every pixel matters when the browser is where you spend your day. 
  • The refreshed color palette is inspired by the feeling of fire: the glow around your active tab, deep smoky purples and lighter tones that add warmth.

The voice is warming up, too. Firefox copy is becoming more direct, more human, and sometimes more playful or fiery. Always genuine… because that’s what sets Firefox apart.

The effect is distinctly Firefox: approachable and energetic, while still easy to scan. Under the hood, reusable tokens, components, patterns, and a shared design language make Firefox easier to evolve over time, so new features feel integrated instead of bolted on.

The redesign is most visible on desktop, but the work extends to mobile, too. Shared colors, icons, copy and design foundations help Firefox feel more consistent across devices.

Yours to shape

Firefox has long been the most customizable browser. It’s in our open source DNA. Now we’re adding more ways to make Firefox feel like yours, including new themes and wallpapers. And we’re exploring more customization over time, like controls for the shape of the interface — tabs, components, and related visual treatments. 

Accessibility is a key part of customization. Firefox is being designed with attention to contrast, readability, focus states, keyboard behavior, target sizes, system settings, visual comfort, and how the interface works across themes and windows.

Dark mode, for example, is not just a preference for many people. It is their default environment. For some, it helps reduce eye strain. For others, it is part of a broader system setup. 

Building in public

Firefox has always been built in the open, and with the help of a global community of contributors and supporters. You help us build the browser, test, extend and improve it. You tell us when something doesn’t feel right. That relationship is part of what makes Firefox different.

While the new design system for Firefox is still being shaped, keep telling us what feels right, and what gets in your way. We’re listening.

Take control of your internet

Download Firefox

The post Designing Firefox for the future appeared first on The Mozilla Blog.

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.

Spidermonkey Development Blog

Saying goodbye to asm.js

Axe-time, sword-time, shields are sundered,
Wind-time, wolf-time, ere the world falls.
Völuspá, Poetic Edda

As of Firefox 148, SpiderMonkey’s asm.js optimizations are disabled by default, and we plan to remove the code entirely in a future release.

If you maintain a site that uses asm.js, nothing will break. asm.js is just a subset of plain JavaScript, so the code keeps running through our regular JIT just like any other script. That said, recompiling to WebAssembly will get you faster execution and smaller binaries.

History

asm.js was Mozilla’s response to the question posed by NaCl and PNaCl: how can the web run code at native speeds?

The idea was clever: pick a strict, statically-typed subset of JavaScript that an engine could recognize on the fly and compile down to native code. We could get performance similar to NaCl/PNaCl and still have code live inside web content and use web API’s (no separate sandbox, IPC, or alternative API’s).

asm.js shipped in Firefox 22 back in 2013 and was a success. It let projects like Unity and Unreal ship C/C++ codebases to the web for the first time, using just standard web technologies. The Epic Citadel demo was ported to the web in just four days. It was a landmark achievement, and a fond memory for the original asm.js team.

asm.js proved that we could run code at near-native speed on the web using just web technologies. This opened the door to WebAssembly, which shipped several years later in Firefox 52. Without asm.js, we likely wouldn’t have WebAssembly.

Why now?

So why turn it off? WebAssembly has succeeded, and asm.js usage has mostly migrated over. Keeping the asm.js path alongside WebAssembly costs us maintenance time and gives us extra attack surface in the VM.

If you are shipping asm.js content, please consider recompiling to WebAssembly! Our WebAssembly pipeline is significantly more advanced than the asm.js one ever was. You should see faster execution and smaller binaries.

Ragnarök

The asm.js compiler is called OdinMonkey. As was foretold long ago, OdinMonkey must meet his fated doom. The bug Ragnarök tracks the “Twilight of OdinMonkey”.

All is not lost however, for born of OdinMonkey is BaldrMonkey, our WebAssembly optimizing compiler. OdinMonkey may be swallowed whole by the wolf, Fenrir, but BaldrMonkey will rule over the reborn world alongside RabaldrMonkey (“commotion”), our WebAssembly baseline compiler.

On this Odin’s day (Wednesday) we thank OdinMonkey for thirteen years of service. Skål!

Then fields unsowed bear ripened fruit,
all ills grow better, and Baldr comes back;
Baldr and Hoth dwell in Hropt’s battle-hall. – Völuspá, Poetic Edda

Firefox Nightly

Settings Are Getting a New Look!

The redesign of Firefox Settings is now enabled by default in Nightly! With this change, we are making it easier for people to customize Firefox and discover the controls that matter most to them. The redesign improves navigation and organization, updates labels and descriptions, and uses a new underlying architecture that will make Settings easier to update over time.

Firefox Settings have grown a lot over the years. Our research showed that some areas had become crowded and more difficult to navigate, and that important controls could be hard to discover. Some terminology and layouts had also become inconsistent over time. Internally, the existing structure also made Settings harder to maintain and evolve over time.

Here are some of the changes you’ll notice:

  • More pages within Settings, with less content per page
  • Clearer grouping and better flow between related controls
  • Improved labels and helper text
  • Consistent use of our design system throughout
New Settings navigation and layout in Nightly

The new Settings navigation and layout

While the organization of Settings has changed, all your existing choices and preferences remain in place. You can always use the search bar to jump straight to what you’re looking for.

We plan to ship the redesign to the Release channel in Firefox 152. You will continue to see changes in Nightly, and we welcome feedback, especially in Mozilla Connect.

Customization has long been a core part of Firefox, and this redesign is intended to continue improving how people discover and manage their browser settings. We appreciate all of the feedback from the community so far on this project and look forward to refining the experience with your input!

This Week In Rust

This Week in Rust 652

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
Newsletters
Project/Tooling Updates
Observations/Thoughts
Rust Walkthroughs

Crate of the Week

This week's crate is cargo-crap, a cargo subcommand to calculate the Change Risk Anti-Patterns metric for a crate.

Despite a lamentable lack of suggestions, llogiq is pleased with his choice.

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

369 pull requests were merged in the last week

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

Fewer than usual PRs merged, mostly due to a shorter week than normal and some CI trouble. Overall a slightly positive week for performance.

Triage done by @simulacrum. Revision range: 29b75901..281c97c3

0 Regressions, 0 Improvements, 4 Mixed; 1 of them in rollups 17 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
Compiler Team (MCPs only)

No Items entered Final Comment Period this week for Rust, Rust RFCs, Cargo, 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-05-20 - 2026-06-17 🦀

Virtual
Asia
Europe
North America
Oceania

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

Posts like this are useful for those of us who like to help, and who work on rustc to make it more helpful, by letting us learn about what kinds of mistakes people make.

Kevin Reid on rust-users

Thanks to firebits.io 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

Jonathan Almeida

My Firefox for Android local build environment

The Firefox for Android app has always had a complicated build process - we're cramping a complex cross-platform browser engine and all the related components that make it work on Android into one package. In its current form, it lives in the Firefox mono-repo at mozilla-central (now mozilla-firefox using the git repository).

I wanted to document my "artifact-mode" environment here since it's worked quite successfully for me for many years with minor changes.

NOTE: After a fresh clone of the mono-repo, don't forget to first run and follow the prompts of ./mach bootstrap .

mozconfig

My mozconfig below is enabled for artifact mode, but occasionally I switch between various configurations. You can see those commented out, with these few extra notes:

  • I like to separate out my objdirs to avoid cache pollution between the different build types. I think you can get away without needing to specify this and an objdir for your build type and arch will be generated.
  • sccache speeds up the native portion of full builds after the first slow one, but it's a hit or miss if you fetch from the remote repository but don't need to rebuild as often.
  • I don't care to manually run the clobber step, and I don't truly appreciate why that isn't always automatically done.
  • Emilio's mozconfig manager looks like a better solution, however my needs are very simple.
# Build GeckoView/Firefox for Android:
ac_add_options --enable-application=mobile/android
# Targeting the following architecture.
# For regular phones, no --target is needed.
# For x86 emulators (and x86 devices, which are uncommon):
# ac_add_options --target=i686
# For newer phones or Apple silicon
ac_add_options --target=aarch64
# For x86_64 emulators (and x86_64 devices, which are even less common):
# ac_add_options --target=x86_64
# sccache will significantly speed up your builds by caching
# compilation results. The Firefox build system will download
# sccache automatically.
# This only works for non-artifact builds.
#ac_add_options --with-ccache=sccache
# Enable artifact builds; manager-mode.
ac_add_options --enable-artifact-builds
# Write build artifacts to..
## Full build dir
#mk_add_options MOZ_OBJDIR=./objdir-droid
#mk_add_options MOZ_OBJDIR=./objdir-desktop
## Artifact builds
mk_add_options MOZ_OBJDIR=./objdir-frontend
# Automatic clobbering; don't ask me.
mk_add_options AUTOCLOBBER=1

JAVA_HOME

Sometimes you might find yourself needing to run a (non-mach) command in the terminal. Those typically will need to invoke some parts of gradle for an Android build, so it's best to make sure those are using the same JDK as the bootstrapped one in the mono-repo. This avoids weird build errors where something that compiles in one place isn't working in another (like Android Studio).

The location for the JDKs are typically in ~/.mozbuild/jdk/, and if you've between around for ~6 months you end up with multiple versions after every JDK bump:

$ ls -l ~/.mozbuild/jdk/
drwxr-xr-x@ - jalmeida 15 Apr  2025 jdk-17.0.15+6
drwxr-xr-x@ - jalmeida 15 Jul  2025 jdk-17.0.16+8
drwxr-xr-x@ - jalmeida 21 Oct  2025 jdk-17.0.17+10
drwxr-xr-x@ - jalmeida 20 Jan 09:00 jdk-17.0.18+8
drwxr-xr-x@ - jalmeida 26 Feb 15:04 mozboot

You can find some way to point your latest JDK to one location or you can be lazy like me and pick the latest version to assign as your JAVA_HOME property by adding this to your shell's RC file:

export JAVA_HOME="$(ls -1dr -- $HOME/.mozbuild/jdk/jdk-* | head -n 1)/Contents/Home"

Android Studio

UPDATE: With D286123 landed, this should no longer be necessary! 🎉

Similarly for Android Studio, let's do the same so that environment is identical. Head to, Settings | Build, Execution, Deployment | Build Tools | Gradle, and ensure that "Gradle JDK" path is set to JAVA_HOME.

Lately, the default seems to be for it to follow GRADLE_LOCAL_JAVA_HOME which is a property we can't easily override, so we have to manually set this ourselves.

Using the same Android SDK also helps speed things up and avoids source confusion. You can typically find it in ~/.mozbuild/android-sdk-macosx and update it at Settings | Languages & Frameworks | Android SDK.

Debugging

This section is for miscellaneous build error situations that come-up, but assuming mach build work and there are no known Android build changes, my solution has typically always been the same.

For example, the other day I fetched another engineers patch to test out locally1 as part of reviewing it where I faced the error message below:

Execution failed for task ':components:feature-pwa:compileDebugKotlin'.
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':components:feature-pwa:compileDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction
   > Internal compiler error. See log for more details
* Try:
> Run with --info or --debug option to get more log output.
> Run with --scan to generate a Build Scan (powered by Develocity).
> Get more help at https://help.gradle.org.
* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':components:feature-pwa:compileDebugKotlin'.
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:135)
	at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:288)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:133)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:121)
	at org.gradle.api.internal.tasks.execution.ProblemsTaskPathTrackingTaskExecuter.execute(ProblemsTaskPathTrackingTaskExecuter.java:41)
	at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
	at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:74)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
	at org.gradle.execution.plan.DefaultNodeExecutor.executeLocalTaskNode(DefaultNodeExecutor.java:55)
	at org.gradle.execution.plan.DefaultNodeExecutor.execute(DefaultNodeExecutor.java:34)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:355)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:343)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.lambda$execute$0(DefaultTaskExecutionGraph.java:339)
	at org.gradle.internal.operations.CurrentBuildOperationRef.with(CurrentBuildOperationRef.java:84)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:339)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:328)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:459)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:376)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
Caused by: org.gradle.workers.internal.DefaultWorkerExecutor$WorkExecutionException: A failure occurred while executing org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction
	at org.gradle.workers.internal.DefaultWorkerExecutor$WorkItemExecution.waitForCompletion(DefaultWorkerExecutor.java:289)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.lambda$waitForItemsAndGatherFailures$2(DefaultAsyncWorkTracker.java:130)
	at org.gradle.internal.Factories$1.create(Factories.java:33)
	at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withoutLocks$2(DefaultWorkerLeaseService.java:344)
	at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:42)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLocks(DefaultWorkerLeaseService.java:342)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLocks(DefaultWorkerLeaseService.java:326)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLock(DefaultWorkerLeaseService.java:331)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:126)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:92)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForAll(DefaultAsyncWorkTracker.java:78)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForCompletion(DefaultAsyncWorkTracker.java:66)
	at org.gradle.api.internal.tasks.execution.TaskExecution$3.run(TaskExecution.java:260)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:237)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:220)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecution.java:203)
	at org.gradle.api.internal.tasks.execution.TaskExecution.execute(TaskExecution.java:170)
	at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:105)
	at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:44)
	at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:59)
	at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:56)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:56)
	at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:44)
	at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:42)
	at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:75)
	at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
	at org.gradle.internal.execution.steps.PreCreateOutputParentsStep.execute(PreCreateOutputParentsStep.java:50)
	at org.gradle.internal.execution.steps.PreCreateOutputParentsStep.execute(PreCreateOutputParentsStep.java:28)
	at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:68)
	at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:38)
	at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:61)
	at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:26)
	at org.gradle.internal.execution.steps.CaptureOutputsAfterExecutionStep.execute(CaptureOutputsAfterExecutionStep.java:69)
	at org.gradle.internal.execution.steps.CaptureOutputsAfterExecutionStep.execute(CaptureOutputsAfterExecutionStep.java:46)
	at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:39)
	at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:28)
	at org.gradle.internal.execution.steps.BuildCacheStep.executeWithoutCache(BuildCacheStep.java:189)
	at org.gradle.internal.execution.steps.BuildCacheStep.lambda$execute$1(BuildCacheStep.java:75)
	at org.gradle.internal.Either$Right.fold(Either.java:176)
	at org.gradle.internal.execution.caching.CachingState.fold(CachingState.java:62)
	at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:73)
	at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:48)
	at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:46)
	at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:35)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:75)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$2(SkipUpToDateStep.java:53)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:53)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:35)
	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:37)
	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:27)
	at org.gradle.internal.execution.steps.ResolveIncrementalCachingStateStep.executeDelegate(ResolveIncrementalCachingStateStep.java:49)
	at org.gradle.internal.execution.steps.ResolveIncrementalCachingStateStep.executeDelegate(ResolveIncrementalCachingStateStep.java:27)
	at org.gradle.internal.execution.steps.AbstractResolveCachingStateStep.execute(AbstractResolveCachingStateStep.java:71)
	at org.gradle.internal.execution.steps.AbstractResolveCachingStateStep.execute(AbstractResolveCachingStateStep.java:39)
	at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:64)
	at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:35)
	at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:62)
	at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:40)
	at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStep.java:76)
	at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStep.java:45)
	at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.executeWithNonEmptySources(AbstractSkipEmptyWorkStep.java:136)
	at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.execute(AbstractSkipEmptyWorkStep.java:66)
	at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.execute(AbstractSkipEmptyWorkStep.java:38)
	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
	at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:36)
	at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:23)
	at org.gradle.internal.execution.steps.HandleStaleOutputsStep.execute(HandleStaleOutputsStep.java:75)
	at org.gradle.internal.execution.steps.HandleStaleOutputsStep.execute(HandleStaleOutputsStep.java:41)
	at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.lambda$execute$0(AssignMutableWorkspaceStep.java:35)
	at org.gradle.api.internal.tasks.execution.TaskExecution$4.withWorkspace(TaskExecution.java:297)
	at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStep.java:31)
	at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStep.java:22)
	at org.gradle.internal.execution.steps.ChoosePipelineStep.execute(ChoosePipelineStep.java:40)
	at org.gradle.internal.execution.steps.ChoosePipelineStep.execute(ChoosePipelineStep.java:23)
	at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.lambda$execute$2(ExecuteWorkBuildOperationFiringStep.java:67)
	at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.execute(ExecuteWorkBuildOperationFiringStep.java:67)
	at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.execute(ExecuteWorkBuildOperationFiringStep.java:39)
	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:46)
	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:34)
	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:44)
	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:31)
	at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:64)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:132)
	... 30 more
Caused by: org.jetbrains.kotlin.gradle.tasks.FailedCompilationException: Internal compiler error. See log for more details
	at org.jetbrains.kotlin.gradle.tasks.TasksUtilsKt.throwExceptionIfCompilationFailed(tasksUtils.kt:22)
	at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:112)
	at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:75)
	at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:68)
	at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:64)
	at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:61)
	at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
	at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:61)
	at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
	at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
	at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:58)
	at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:176)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
	at org.gradle.internal.Factories$1.create(Factories.java:33)
	at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withLocksAcquired$0(DefaultWorkerLeaseService.java:269)
	at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:42)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocksAcquired(DefaultWorkerLeaseService.java:267)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:259)
	at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
	at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
	... 2 more

The full trace was long and didn't seem related to a code failure in the module itself. So I employed the solution, which is always the same:

  1. ./mach build
  2. In Android Studio, File > Sync Project with Gradle Files.

Yup, that's all. Very simple and boring.


1

With Jujutsu, this is the moz-phab command I use which has made it easier to manage review patches: moz-phab patch <patch-id> --no-branch --apply-to main@origin

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/116197244320129422">the original post</a> on Mastodon.</p></noscript>
<noscript>You need JavaScript to view the comments.</noscript> &>"'

The Mozilla Blog

A free VPN you can trust, now built into Firefox

Update on May 19, 2026: Firefox’s free built-in VPN now supports location selection, giving people access to a fully comprehensive VPN experience directly within the browser. Starting today, Firefox users in the U.S., U.K., France, Germany, and Canada can choose to browse from any of the countries where we’ve launched VPN support.  Browsing is subject to the local laws and content restrictions of the selected region.

Original post from March 2026:

Today we’re introducing a free built-in VPN in Firefox, a new IP-protection feature designed to keep you even more private while you browse. We’re starting by offering an industry-leading 50 gigabytes of free VPN-browsing each month. 

Firefox has long focused on building privacy tools directly into the browser to protect you online. Over the years, we’ve introduced world-class protections that block known trackers, reduce fingerprinting and limit how companies can follow people across the web. Our goal has been consistent: make meaningful privacy protections accessible to Firefox users every day.

Firefox is the only major browser to include a built-in VPN like this for free — giving you more control over your privacy, right where you browse.

Privacy built into the browser

Every time you visit a website, your IP address is shared automatically. IP addresses help websites know where to send information back to your device, but they can also be used to approximate your location, link your browsing activity across sites and keep logs about your online behavior, meaning websites can track your behavior. It’s one of many ways companies track activity across the internet.

Additionally, when you’re using public Wi-Fi while at a coffee shop, in a hotel, or in your dorm, people can spy on your network traffic and see which websites you might be visiting. 

At Mozilla, we believe people should have stronger protections against this kind of tracking and spying, and that those protections should be easy to use.

Introducing built-in VPN

Our free built-in VPN is designed to make IP protection simple to use in Firefox.

The built-in VPN includes an unprecedented 50 GB per month of free VPN browsing, enough to cover everyday activities like shopping, banking, and reading.

Turn it on in Firefox with a single click. No extra apps. No downloads. Once it’s on, Firefox routes your browsing traffic through a proxy network that replaces your IP address before it reaches a website. The sites you visit see the proxy’s IP address rather than your own. Firefox already encrypts your traffic with HTTPS, but masking your IP adds another layer of privacy. You can mask the URLs you’re visiting from anyone trying to spy on your network traffic on public Wi-Fi, like while you’re enjoying a latte at your favorite coffee shop. 

If you reach the monthly limit, IP protection is paused until the next cycle. Firefox will require you to confirm before proceeding without the VPN so your browsing doesn’t unintentionally continue without IP protection.

Browser-level protection and full-device protection

The free built-in VPN helps secure your traffic while browsing in Firefox, making it a simple way to protect your IP address from being tracked by big tech. However, it does not offer full device protection. 

For those looking for broader coverage, you can also choose protection that extends across your entire device, including other apps. The standalone Mozilla VPN subscription offers this capability with unlimited data across multiple devices. Depending on your needs, you can pick the level of privacy and protection that suits you. 

We’ve heard concerns about so-called “free VPNs,” which often rely on advertising or selling user data to generate revenue. Firefox’s built-in VPN is designed differently. It does not sell your browsing data and does not inject advertising into your traffic. Instead, we offer a limited amount of browser-level protection for free, alongside Mozilla VPN, our paid, unlimited, full-device VPN service. 

Read more about the differences between VPNs and web proxies.

Rolling out to Firefox users

The free built-in VPN is currently rolling out as a beta to Firefox desktop users in the United States, the United Kingdom, Germany and France, with plans to expand to additional countries coming soon over the next several releases.

As with many Firefox features, we’re introducing it gradually starting in Firefox 149 so we can learn from user feedback and continue improving the experience.

Building a more private web

Protecting privacy online is an ongoing effort. As the web evolves, new technologies create both opportunities and challenges for keeping personal information safe.

Mozilla has spent years building privacy protections — from Total Cookie Protection to Private browsing mode to anti-fingerprinting — directly into Firefox so people have more control over how they experience the web. This built-in VPN is one more way Firefox helps you browse with less exposure and more peace of mind.

By continuing to build these protections into Firefox, we aim to make the web safer, more transparent and more respectful of the people who use it.

Take control of your internet

Download Firefox

The post A free VPN you can trust, now built into Firefox appeared first on The Mozilla Blog.

The Mozilla Blog

New in Firefox 151: VPN location selection, AI controls on mobile, and expanded Shake to Summarize support

Today, Firefox is rolling out updates across desktop and mobile that give you more choice over how you browse.

Here’s a look at what’s new.

Adding location selection to Firefox’s free VPN

Firefox now offers a fully featured VPN experience directly in the browser — for free. In just two months, over 1 million users have already signed up to try it. 

With the addition of location selection, one of the most requested features from the Firefox community, Firefox’s free built-in VPN now delivers all the flexibility and functionality people expect from a modern VPN. 

Starting today, Firefox users in the U.S., U.K., France, Germany, and Canada can choose to browse from any of the countries where we’ve launched VPN support. Whether you want to view local news, shop regionally, or see how content appears in another country, location selection gives you more control over where your connection appears from online.

Firefox will recommend the location closest to you by default, but people can switch locations at any time. Additional locations are planned for future releases. Browsing will still be subject to the local laws and content restrictions of the selected region.

Available in Firefox 151.

More controls over AI features on mobile

You can now decide how AI shows up in your Firefox mobile browsing experience, with more control over which AI features to activate and more ways to use them across devices and languages.

After launching AI controls on desktop earlier this year, Firefox is bringing them to iOS and Android. Depending on your device and region, available controls include features like translations, voice search and Shake to Summarize. Preferences can be updated at any time, making it easy to keep only the tools you want to use as new AI-powered features arrive in Firefox.

Shake to Summarize, recognized by TIME as one of the Best Inventions of 2025, helps you cut to the chase on mobile webpages by generating a simple summary with a flick of your wrist. The feature is now expanding to Android devices, with English rolling out first and additional languages coming soon. Shake to Summarize is now available on iOS in English, German, French, Spanish, Portuguese, Italian, and Japanese, giving more people the option to use it in the language that works best for them.

AI controls are available on iOS and Android. Shake to Summarize is available on Android in English and on iOS in English, German, French, Spanish, Portuguese, Italian, and Japanese.

A one-click reset for Private Browsing

Finished shopping for a surprise gift or logging into a temporary account? A new flame-shaped “Clear Private Session” button, located to the right of the address bar, instantly clears your current private session and starts a new one without requiring you to close and reopen the browser. Cookies, browsing history, logins, and other session data from your private windows are deleted automatically. Read more in this week’s release notes.

Try it out in Firefox 151.

Coming soon in Nightly: A refreshed settings page

Firefox is previewing a redesigned settings page in Nightly that makes it easier to find the controls you’re looking for and move through settings more intuitively. The redesign introduces improved navigation, clearer organization, and updated labels and descriptions designed to make customization easier. 

Existing settings and preferences will remain unchanged, and the updated search experience helps people quickly find settings that may appear in new sections.

This will be enabled by default for Nightly users starting later this week.

Download the latest version of Firefox to explore what’s new and share feedback with us on Mozilla Connect.

Take control of your internet

Download Firefox

The post New in Firefox 151: VPN location selection, AI controls on mobile, and expanded Shake to Summarize support appeared first on The Mozilla Blog.

The Mozilla Blog

AI controls are here for Firefox mobile

Mobile browsing is personal. It’s the link you open from a group chat because someone said, “Wait, is this real?” It’s the article you read in the few quiet minutes you have to yourself. It’s the review you skim before buying something you’ve been thinking about all week.

On a phone, browsing follows you through the day, from the first thing you look up in the morning to the last tab you close at night. So when AI features are part of the experience, you should have a clear way to choose what helps and what stays off.

That’s why we’re bringing AI controls – a popular desktop feature on Firefox – to iOS and Android users. AI controls let you turn off Firefox’s AI features entirely, enable only the ones you want or adjust your choices later. The feature rolls out on mobile on May 19.

AI features you can choose on Firefox mobile

What you see may vary by device and location, but AI controls in Firefox mobile let you turn on or off features, including website translations and voice searchon Android, and translations and Shake to Summarize on iOS, where available.

As Firefox adds new features, you can come back to your settings and choose what works for you.

Mobile browsing on your terms

As AI becomes a bigger part of the web, the question isn’t just what it can do. It’s how much control people have over it.

In Firefox mobile, AI can help make browsing easier, from finding what you need to understanding pages faster. But Firefox isn’t just adding AI features. We’re adding the controls, too.

Decide how AI fits into your experience. Try the features you want. Turn off the ones you don’t. Keep browsing on your terms.

Take Firefox with you

Download Firefox mobile

The post AI controls are here for Firefox mobile appeared first on The Mozilla Blog.

Firefox Developer Experience

Firefox WebDriver Newsletter 151

WebDriver is a remote control interface that enables introspection and control of user agents. As such, it can help developers to verify that their websites are working and performing well with all major browsers. The protocol is standardized by the W3C and consists of two separate specifications: WebDriver classic (HTTP) and the new WebDriver BiDi (Bi-Directional).

This newsletter gives an overview of the work we’ve done as part of the Firefox 151 release cycle.

Contributions

Firefox is an open source project, and we are always happy to receive external code contributions to our WebDriver implementation. We want to give special thanks to everyone who filed issues, bugs and submitted patches.

In Firefox 151, Armin Ulrich contributed a fix to WebDriver BiDi:

WebDriver code is written in JavaScript, Python, and Rust so any web developer can contribute! Read how to setup the work environment and check the list of mentored issues for Marionette, or the list of mentored JavaScript bugs for WebDriver BiDi. Join our chatroom if you need any help to get started!

General

WebDriver BiDi

Marionette

The Mozilla Blog

Firefox’s Shake to Summarize expands to Android and new languages on iOS

Don’t let bloated websites slow you down. When you just need the gist, scrolling through ads and filler content can turn a quick check into an endless scroll.

Firefox’s Shake to Summarize feature solves that. We first launched it on iOS in English last September, earning a special mention in TIME’s Best Inventions of 2025 and a strong response from users. Since then, we’ve expanded it to iOS users in German, French, Spanish, Portuguese, Italian and Japanese. Starting today, we’re bringing it to Android users in English, with more languages coming soon.

Get the gist in seconds

On any web page under 5,000 words, just shake your phone and a clean summary will instantly appear.

If you prefer tapping, you can select Summarize Page under More in the three-dot menu.

Powered by AI, protected by Firefox

To keep your data secure, Shake to Summarize uses different technology depending on your device:

  • On iPhone 15 Pro or later (running iOS 26+): Your summary is generated right on your device using Apple Intelligence.
  • On all other devices: Your text is sent securely to Mozilla’s cloud-based AI. We power this with Mistral-Small, an AI model carefully selected for its speed, efficiency, and alignment with an open internet

You can read more about theAI powering Firefox’s Shake to Summarize here.

Don’t settle for the default

Your phone’s default browser leaves you stuck scrolling through cluttered pages and content overload.

The Firefox mobile team is taking a purposeful approach – with smart tools that respect your time, privacy and choices. Download Firefox today to try Shake to Summarize, and get ready for even more features designed to help you move faster and protect your focus.

Take Firefox with you

Download Firefox mobile

The post Firefox’s Shake to Summarize expands to Android and new languages on iOS appeared first on The Mozilla Blog.

The Rust Programming Language Blog

Project goals update — April 2026 (end of 2025H2)

The 2025H2 Project Goal period has now concluded. Over these months, the Rust Project pursued 41 Project Goals, 13 of which were designated as Flagship Goals. This post contains curated updates on our progress since the last post and the final status for each of the goals (many of which continue as part of the 2026 period). Full details for any particular goal are available in its tracking issue.

Thanks to everyone who contributed! <3

Table of contents


Flagship: Beyond the &

Continue Experimentation with Pin Ergonomics

3 detailed updates available.

Design a language feature to solve Field Projections

5 detailed updates available.
  • Benno Lossincomment from 2026-01-01

  • Benno Lossincomment from 2026-01-25

    Earlier this month, Nadrieril Ding Xiang Fei and I held a meeting on autoref and method resolution in a world with field projections. This meeting resulted in a new page for the wiki on autoref.

  • Benno Lossincomment from 2026-02-28

    The first pull request of the lang experiment has just been merged: rust-lang/rust#152730

    This PR enables the use of the field_of! macro to obtain a unique type for each field of a struct, enum variant, tuple, or union. We call these types field representing types (FRTs). When the base type is a struct that is not repr(packed), only contains Sized fields, this type automatically implements the Field trait that exposes some information about the field to the type system. The offset in bytes from the start of the struct, the type of the field and the type of the base type.

    The feature is still incomplete and highly experimental. We also want to tackle the limitations in future PRs. For the moment this is enough to give us the ability to experiment with library versions of field projections and write functions that are generic over the fields of structs. For example one can write code like this:

    #![feature(field_projections)]
    
    use std::field::{Field, field_of};
    use std::ptr;
    
    fn project_ref<'a, T, F: Field<Base = T>>(r: &'a T) -> &'a F::Type {
        // SAFETY: the `Field` trait guarantees that this is sound.
        unsafe { &*ptr::from_ref(r).byte_add(F::OFFSET).cast() }
    }
    
    struct Struct {
        field: i32,
        other: u32,
    }
    
    fn main() {
        let s = Struct { field: 42, other: 24 };
        let r = &s;
        let field = project_ref::<_, field_of!(Struct, field)>(r);
        let other = project_ref::<_, field_of!(Struct, other)>(r);
        println!("field: {field}"); // prints 42
        println!("other: {other}"); // prints 24
    }

    A very important feature of the types returned by field_of! is that you can implement traits for them if you own the base type. This allows anointing fields with information by extending the Field trait. For example, this allows encoding the property of being a structurally pinned field:

    use std::pin::Pin;
    
    unsafe trait PinnableField: Field {
        type StructuralRefMut<'a>
        where
            Self::Type: 'a,
            Self::Base: 'a;
    
        fn project_mut<'a>(base: Pin<&'a mut Self::Base>) -> Self::StructuralRefMut<'a>
        where
            Self::Type: 'a,
            Self::Base: 'a;
    }
    
    fn project_pinned<'a, T, F>(r: Pin<&'a mut T>) -> <F as PinnableField>::StructuralRefMut<'a>
    where
        F: PinnableField<Base = T>,
    {
        F::project_mut(r)
    }

    We can then implement this extra trait for all of the fields of our struct (and automate that with a proc-macro):

    unsafe impl PinnableField for field_of!(Struct, field) {
        type StructuralRefMut<'a> = &'a mut i32;
    
        fn project_mut<'a>(base: Pin<&'a mut Self::Base>) -> Self::StructuralRefMut<'a>
        where
            Self::Type: 'a,
            Self::Base: 'a,
        {
            let base = unsafe { Pin::into_inner_unchecked(base) };
            &mut base.field
        }
    }
    
    unsafe impl PinnableField for field_of!(Struct, other) {
        type StructuralRefMut<'a> = Pin<&'a mut u32>;
        // u32 is `Unpin`, so this isn't doing anything special, but it highlights the pattern.
    
        fn project_mut<'a>(base: Pin<&'a mut Self::Base>) -> Self::StructuralRefMut<'a>
        where
            Self::Type: 'a,
            Self::Base: 'a,
        {
            let base = unsafe { Pin::into_inner_unchecked(base) };
            unsafe { Pin::new_unchecked(&mut base.other) }
        }
    }

    Now you can safely obtain a pinned mutable reference to other and a normal mutable reference to field by calling the project_pinned function and supplying the correct FRT.

    (playground link)

  • Benno Lossincomment from 2026-03-20

    Plan for 2026

    We have an updated plan for this goal in 2026 consisting of three major steps:

    • a-mir-formality,
    • Implementation,
    • Experimentation.

    Some of their subtasks depend on other subtasks for other steps. You can find the details in the updated tracking issue. Here is a short rundown of each:

    a-mir-formality: we want to create a formal model of the borrow checker changes we're proposing to ensure correctness. We also want to create a document explaining our model in a more human-friendly language. To really get started with this, we're blocked on the new expression based syntax in development by Niko.

    Implementation: at the same time, we can start implementing more parts in the compiler. We will continue to improve FRTs, while keeping in mind that we might remove them if they end up being unnecessary. They still pose for a useful feature, but they might be orthogonal to field projections. We plan to make small and incremental changes, starting with library additions. We also want to begin exploring potential desugarings, for which we will add some manual and low level macros. When we have that figured out, we can fast-track syntax changes. When we have a sufficiently mature formal model of the borrow checker integration, we will port it to the compiler. After further evaluation, we can think about removing the incomplete_feature flag.

    Experimentation: after each compiler or standard library change, we look to several projects to stress-test our ideas in real code. I will take care of experimentation in the Linux kernel, while Tyler Mandry will be taking a look at testing field projections with crubit. Josh Triplett also has expressed eagerness of introducing them in the standard library; I will coordinate with him and the rest of t-libs-api to experiment there.

  • Benno Lossincomment from 2026-04-02

    Yesterday, we held a t-lang design meeting on our current approach. Nadrieril and I authored a design document with the feedback of Tyler Mandry, Ding Xiang Fei, Alice Ryhl, and Gary Guo. In this document, we provided the motivation for this feature, what the look and feel of a solution fitting into the existing features of Rust is, and a comprehensive + compact introduction to our current approach based on virtual places.

    The general reception was extremely positive. To give some concrete quotes from the meeting:

    • Josh:

      I adore this! I love how orthogonal it is, and how impactful and universal it is. I anticipate this becoming a beloved, pervasive feature of Rust.

      Places and projection seem important enough to me that they're worth giving one of our precious remaining ASCII sigils to, and @ is nicely evocative of a place (something is at a place). So to the extent the final syntax benefits from a sigil, :+1: for giving this @. (See some feedback below on the details, though.)

    • TC:

      Love it. High concept. As I said in the last meeting:

      I particularly like language features that reduce the need for library surface area, and this is one of those.

      There are, of course, many details to resolve and understand further, e.g., with respect to migration issues, interaction with const, async, and other effect-like things, etc. I'm looking forward to seeing the formalization work.

    • tmandry:

      What I love about this direction is how effectively it builds on what Rust already has. I love to see designs that reinforce our existing concepts while pushing them in directions that make them more expressive.

    • Jack:

      Whoo boy. This is great. There's so much here that I'm not exactly sure where to begin and what to comment on. I think this is the type of thing that we will only really be able to figure out the nitty gritty details and ergonomics only after some amount of experimentation.

    There are a few takeaways from this meeting:

    • Mark raised the concern that t-libs should be more involved in reviewing the experimental traits that we intend to add. Ensuring that we don't accidentally stabilize or expose some behavior, have sufficient documentation on our experimental traits, and that t-libs is in the loop of this feature in general.
      • Mark offered to review PRs and I will be tagging him in those.
    • Jack raised the concern that increasing the cognitive load for the 95% use-case should be avoided. Making the right choice between @ and & might be challenging for users.
      • We discussed this point more in the meeting and concluded with that we need to do some experimentation, possibly utilizing the user research team. We will of course keep this in mind and revisit it later when we have a partially working implementation.
    • TC requested that we publish our fine-grained design axioms, essentially the list of things we go through when considering a modification of our proposal.
      • I will write an update on this issue explaining exactly those.

    Aside from the concerns and directly actionable items, the meeting also covered design questions/comments that we want to take a look at in the coming weeks/months:

    Thanks to everyone who participated in the meeting!

Reborrow traits

1 detailed update available.
  • Aapo Alasuutaricomment from 2026-02-28

    PR open to get the first working version of the Reborrow and CoerceShared traits merged.

    Blockers

    Currently "blocked" on PR review, and of course my (and Ding's) work to fix all review issues.

    The review has brought up an opportunity to replace Rvalue::Ref / ExprKind::Ref with a more generalised variant that could encompass both references and user-defined references. This would be powerful, but it would be a very big and scary change. If this turns out to be a blocking issue for reviewers, then this will block the goal for the foreseeable future as the PR then starts on a massive refactoring.

    Help wanted

    The PR currently does not include derive traits, but we'd really want them. Instead of these:

    impl<'a> Reborrow for CustomMarker<'a> {}
    impl<'a> CoerceShared<CustomMarkerRef<'a>> for CustomMarker<a'> {}
    
    impl<'a, T> Reborrow for CustomMut<'a, T> {}
    impl<'a, T> CoerceShared<CustomRef<'a, T>> for CustomMut<'a, T> {}

    we'd prefer to have something like this:

    #[derive(Reborrow, CoerceShared(CustomMarkerRef))]
    struct CustomMarker<'a> { ... }
    
    #[derive(Reborrow, CoerceShared(CustomRef))]
    struct CustomMut<'a, T> { ... }

    If anyone feels like picking up this thread, that'd be awesome: the derive macros do not need to really perform any validity checking, as the trait itself will do that.

    If the PR merges soon, then public testing and exploration of the traits will be the next big thing. Likely concurrently with that the massive refactoring to generalise Rvalue::Ref / ExprKind::Ref.

Flagship: Flexible, fast(er) compilation

build-std

4 detailed updates available.

Production-ready cranelift backend

Promoting Parallel Front End

Relink don't Rebuild

Flagship: Higher-level Rust

Ergonomic ref-counting: RFC decision and preview

Stabilize cargo-script

3 detailed updates available.

Flagship: Unblocking dormant traits

Evolving trait hierarchies

In-place initialization

1 detailed update available.

Next-generation trait solver

1 detailed update available.

Stabilizable Polonius support on nightly

2 detailed updates available.
  • Rémy Rakiccomment from 2026-01-30

    This month's update:

    • tiif is making progress on normalizing opaques while computing implied bounds
    • we discussed how to investigate and fix the remaining correctness issues in Tage's work, to be able to evaluate it more accurately: in particular around variance and bidirectional edges, and without the reliance on NLL (having computed region values / errors)
    • we've tried to see if it'd be possible to remove the cfg region elements
    • Amanda is still working on her two papers, one about the current borrow checker and one about the work on Polonius. Her major PR for the restructuring of placeholder handling during region inference is stalled due to a conflict with further trait solver developments and may have to be abandoned. Work with the larger types team is ongoing and smaller patches/refactorings/improvements are being landed in the meantime.
    • #149639 has now landed, and #150551 is still in review
    • I've also fixed more small inefficiencies (computing boring/relevant locals on-demand in diagnostics, removed conversions between locations and points, etc) building on top of the previous PRs (so they need to be reviewed first)
    • I've looked at crates.io again with the alpha, to find functions that are slower than with NLLs. AFAICT the worst case there is 60% for a 5KLOC function with 42K loans, 255K statements, and 125K outlives constraints. I'll see what we can do with this. Small composable functions is still good advice.
    • there seem to be optimization opportunities to 1. limit propagation to the smaller number of blocks that could be affected by bidirectional edges, 2. for unifying invariant lifetimes of live locals that are assigned at most once (à la use-def chains), 3. for invalidations that are just the activation of a reservation
    • we discussed possible plans to gather actual statistics, using the infrastructure that was created for the Metrics project
    • we're also preparing the new project goal for this year, where we'll want to stabilize the alpha 🤞
  • Rémy Rakiccomment from 2026-02-28

    We had a bit less time this month, the update will be shorter, but still meaningful I hope:

    • #150551 has landed, and it feels stabilizable. To me, this part of the goal is achieved.
    • still, "stabilizable" is not stable, and there is more work to do. We plan to stabilize this year, and the project goal proposal for 2026 tracks how.
    • tiif is still deep in #152051, and a-mir-formality work with Niko and I.
    • Amanda has opened a few cleanup PRs (#152438, and #152579), and #151863 has landed already. She also has started looking into Tage's old PR to see if we can fix it, benchmark it more accurately, and see the cool parts there that we could be using.
    • Jack is possibly going to have some time to work with us this year! His help will be very welcome, especially as I will have less time available myself.
    • we'll be tracking the opaque type region liveness soundness issue in #153215, and I've added a couple tests, in case tiif's PR or anything that impacts them lands.
    • some of the tiny cleanups I mentioned last time have also landed in #152587.

Other goal updates

Add a team charter for rustdoc team

Borrow checking in a-mir-formality

C++/Rust Interop Problem Space Mapping

5 detailed updates available.
  • Joel Marceycomment from 2026-01-20

    The Rust Foundation is opening up a short-term, approximately 3-month, contracting role to assist in our Rust/C++ Interop initiative. The primary work and deliverables for the role will be to make substantial progress on the Problem Space Mapping Rust Project Goal by collecting discrete problem statements and offering up recommendations on the work that should follow based upon the problems that you found.

    If you are interested in how programming languages interoperate, are curious in understanding the problems therein, and are have a passion to think about how those problems may be resolved for the betterment of interop, then this work may be for you.

    An ideal candidate will have experience with Rust programming. Having experience in C++ is strongly preferred as well. If you have direct experience with actual engineering that required interoperating between Rust and C++ codebases, that's even better.

    If you are interested, please email me (email address found in my GitHub profile) or contact me directly on Zulip by Tuesday, January 27 and we can take it from there to see if there may be a potential fit for further discussion.

    Thank you.

  • Joel Marceycomment from 2026-01-31

    The effort to fill the contracting role to support this project goal is in the process winding down. The interview and discussion process is nearly complete. We expect to make a final decision for the role in early February.

  • teorcomment from 2026-02-27

    Hi, I'm the new contractor on the interop problem space mapping project goal.

    In the last week and a half, I've:

    Next step is prioritising a few of the use cases, then working on related problem statements in more detail.

    Blockers

    Nothing at the moment, still working through the high level mapping of the problem space.

    Help wanted

    Suggestions for more interop use cases would be very welcome, just open a discussion in t-lang/interop and I'll turn it into a ticket. Or go ahead and open a use case ticket directly.

    I'll post an update here every few weeks, you can follow more detailed weekly updates on Zulip.

  • teorcomment from 2026-03-30

    In the last month, I've:

    • met with the lang team, Crubit team, and cxx author, and Joel and Mara have met with the C++ standards working group
    • expanded some draft high-level problem statement summaries, and added code examples
    • added 6 new interop use cases
    • added more relationships between problems/use cases and existing project goals & unstable compiler features
    • prepared for the Rust All Hands, and started mentoring for Outreachy

    Specifically, the last month we've identified and prioritised two high-priority use cases for more detailed work:

    And I analysed the problems / use cases we've collected so far, with priorities, responsible language, and a split into semantics or tooling changes.

    Next step is continuing to work on overloading and build systems in more detail. If you have specific Rust/C/C++ build system blockers, please open a chat or ticket.

    Blockers

    Nothing at the moment, everyone has been extremely helpful, and I'm getting good feedback on use cases, problems, priorities, and Rust language experiments.

  • teorcomment from 2026-05-01

    In the last month, I've:

    Specifically, the last month we've made detailed progress on two high-priority use cases:

    Next step is continuing to work on the overloading experiment, along with RustWeek/All Hands preparation, and collecting feedback during the conference.

    Blockers

    Nothing at the moment. There is a steady stream of new use cases, problems, code examples and Rust language experiment feedback.

Comprehensive niche checks for Rust

Const Generics

6 detailed updates available.
  • Niko Matsakiscomment from 2026-01-27

    Boxy and I have established a regular time to check-in on formalizing this within a-mir-formality. Today we mostly worked on the "model" of const values, starting with this

    #[term]
    pub enum ConstData {
        // Sort of equivalent to `ValTreeKind::Branch`
        #[cast]
        RigidValue(RigidConstData),
    
        // Sort of equivalent to `ValTreeKind::Leaf`
        #[cast]
        Scalar(ScalarValue),
    
        #[variable(ParameterKind::Const)]
        Variable(Variable),
    }
    
    
    
    #[term]
    pub enum ScalarValue {
        #[grammar(u8($v0))]
        U8(u8),
        #[grammar(u16($v0))]
        U16(u16),
        #[grammar(u32($v0))]
        U32(u32),
        #[grammar(u64($v0))]
        U64(u64),
        #[grammar(i8($v0))]
        I8(i8),
        #[grammar(i16($v0))]
        I16(i16),
        #[grammar(i32($v0))]
        I32(i32),
        #[grammar(i64($v0))]
        I64(i64),
        #[grammar($v0)]
        Bool(bool),
        #[grammar(usize($v0))]
        Usize(usize),
        #[grammar(isize($v0))]
        Isize(isize),
    }
    
    
    #[term($name $<parameters> { $,values })]
    pub struct RigidConstData {
        pub name: RigidName,
        pub parameters: Parameters,
        pub values: Vec<Const>,
    }

    i.e., a const value can be a scalar value (as today) or a struct literal like Foo { ... } (which would also cover tuples and things). We got the various tests passing. Huzzah!

  • Boxycomment from 2026-01-30

    In addition to what niko posted previously there's been a lot of other stuff happening. A lot of people have opened PRs to improve mGCA this month: León Orell Valerian Liehr Noah Lev @enthropy7 Kivooeo mu001999 @Human9000-bit Redddy @Keith-Cancel @AprilNEA

    A rough list of things that have been improved for mGCA:

    • Lots of new expressions now supported by mGCA: const constructors, tuple constructor calls, array expressions, tuple expression, literals
    • associated_const_equality has been merged into min_generic_const_args. the former was effectively dependent on the latter already so this just makes it nicer to use the former :)
    • traits can now be dyn compatible if all associated constants are type consts and are specified in the trait object (e.g. dyn Trait<ASSOC = 10>)
    • type consts are enforced to be non-generic
    • a bunch of ICEs have been fixed
    • camelid has been working on "non-min" version of mGCA which will allow arbitrary expressions to be used in the type system (a blog post with more detail will be published once this actually lands)

    In non-mGCA updates, as niko says, we've been meeting regularly to make progress on modelling const generics in a-mir-formality. I've also been spending time thinking about the interactions between adt_const_params and ADTs with privacy/safety invariants and I think I know how to structure the RFC in this area so can make progress on that again

    There's some more detail about the various bits of work people have done and who did what here: #project-const-generics > perfectly adequately sized wins @ 💬

  • Niko Matsakiscomment from 2026-02-13

    Boxy and I have met (and continue to meet) and work on modeling const generics in a-mir-formality. We're still working on laying the groundwork.

    There is a proposed project goal for next year.

  • Boxycomment from 2026-02-28

    There's been a lot of miscellaneous fixes for mGCA this month. I've also started drafting some blog posts to explain what's going on with mGCA/oGCA as well as soliciting use cases/experience reports for them and adt_const_params. I also talked with some folks at Rust Nation this month about const generics and what features would be useful for them and why.

  • Boxycomment from 2026-04-02

    Late on the update :') niko and i continue to meet to discuss const generics. we've made some progress on figuring out problems around privacy/safety in const generics. we've also been discussing the big picture stuff for const generics and where we're "heading".

  • Boxycomment from 2026-05-01

    started running weekly meetings about const generics to make it easier to keep up to date with all the people who are working on const generics stuff. i think min_adt_const_params is now at the point of what the RFC is going to specify.

    GCA is making good progress thanks to ashley's work. i also met with lcnr where we talked about whether there was some version of mGCA that is stabilizeable in the near future or not (maybe!)

Continue resolving cargo-semver-checks blockers for merging into cargo

1 detailed update available.

Develop the capabilities to keep the FLS up to date

2 detailed updates available.
  • Pete LeVasseurcomment from 2026-03-04

    We have a Project Goal in 2026 that we'll take on: Stabilize FLS Release Cadence. Progress towards 1.93.1 looks good, most issues are closed.

    Help wanted

    We'd love more folks from the safety-critical community to contribute to picking up issues or opening an issue if you notice something is missing.

  • Pete LeVasseurcomment from 2026-04-02

    Trying to prepare FLS releases earlier:

    • since we completed the 1.94.0 release of the FLS a bit early this time, we checked into the stretch part of our goal this year to look at 1.95.0 early
    • we learned a bit more of the release notes process thanks to tips from Eric Huss and TC
    • Tshepang Mbambo and I attended the t-release meeting last week where we chatted about working a little "upstream" with them on generating the release notes a bit earlier
    • tomorrow in our t-fls meeting we'll discuss our interest with engaging over there; at a minimum I'll get engaged with t-release

    Glossary and main-body text harmonization:

    • the first PR landed from Tshepang Mbambo removing IDs from the glossary
    • further steps planned, we have a tracking issue for it

    Developer guide:

    • akin to how the Reference now has a developer's guide now for contributing we'll do the same in the FLS
    • Hristian Kirtchev has been working on this

Emit Retags in Codegen

4 detailed updates available.
  • Ian McCormackcomment from 2026-01-09

    Here's our January status update!

    • Yesterday, we posted an MCP for our retag intrinsics. While that's in progress, we'll start adapting our current prototype to remove our dependence on MIR-level retags. Once that's finished, we'll be ready to submit a PR.
    • We published our first monthly blog post about BorrowSanitizer.
    • Our overall goal for 2026 is to transition from a research prototype to a functional tool. Three key features have yet to be implemented: garbage collection, error reporting, and support for atomic memory accesses. Once these are complete, we'll be able to start testing real-world libraries and auditing our results against Miri.
  • Ian McCormackcomment from 2026-02-24

    We just posted our February status update for BorrowSanitizer. TL;DR:

    • We provide detailed error messages for aliasing violations, which look almost like Miri's do!
    • We have two forms of retag intrinsic: __rust_retag_mem and __rust_retag_reg. We no longer require a compiler plugin to determine the permission associated with a retag, which will make it possible to use BorrowSanitizer by providing a single -Zsanitizer=borrow flag to rustc. You can check out our MCP for more detailed design updates.
    • We are starting to have a better understanding of how BorrowSanitizer performs in practice, but we do not have enough data yet to be certain. From one test case, it seems like we are somewhat faster but still in the same category of performance as Miri when we compare against other sanitizers. Expect more detailed results to come as we scale up our benchmarking pipeline.
    • We have a tentative plan for upstreaming BorrowSanitizer in 2026, starting with its LLVM components. We intend to start the RFC process on the LLVM side this spring, once our API is stable.
  • Ian McCormackcomment from 2026-03-30

    We just posted our March status update for BorrowSanitizer. TL;DR:

    • We added hundreds more relevant tests from Miri's test suite. At the moment, 80% pass.
    • We improved our cargo plugin (cargo-bsan) to better support multilanguage libraries. This will let us start to recreate the bugs from our earlier evaluation.

    Our goal for April is to continue expanding our test suite, finish an initial version of the LLVM components of BorrowSanitizer, and hopefully start the RFC process on the LLVM side.

  • Ian McCormackcomment from 2026-04-29

    We have some exciting news: our talk on BorrowSanitizer was accepted at RustConf this year! We’re grateful for the opportunity and looking forward to sharing our results with the broader community this September.

    We just posted our April status update. It’s a bit of a technical one. Here’s the TL;DR:

    • BorrowSanitizer now uses a shadow stack to track metadata at runtime - this is a significantly different strategy than other LLVM sanitizers, and it will help us support garbage collection.
    • We are now ready to start sending in PRs for our retag intrinsics. It will take a little time to split our changes up into meaningful, reviewable chunks—you can expect to see these throughout the next week.

    The RFC for our LLVM components is taking a little longer than expected, but it was worth taking the extra time to test out compiler changes and make sure that we had the core parts of the instrumentation pass settled. We’ll be drafting the RFC throughout the next few weeks.

Expand the Rust Reference to specify more aspects of the Rust language

1 detailed update available.

Finish the libtest json output experiment

Finish the std::offload module

2 detailed updates available.
  • Manuel Drehwaldcomment from 2026-01-16

    std::autodiff is moving closer to nightly, and std::offload is gaining various performance, feature, and hardware support improvements.

    autodiff

    Jakub Beránek, sgasho, and I continued working on enabling autodiff in nightly. We have a PR up that builds autodiff in CI, and verified that the artifacts can be installed and work on Linux. For apple however, we noticed that any autodiff usage hangs. After some investigation, it turns out that we ended up embedding two LLVM copies, one in rustc, and one in Enzyme. It should be comparably easy to get rid of the second one. Once we verified that this fixes the build, we'll merge the PR to enable autodiff on both targets in nightly.

    offload

    A lot of interesting updates on the performance, feature, and hardware support side.

    1. Marcelo Domínguez, @kevinsala, @jdoerfert, and I started implementing the first benchmarks, since that's generally the best way to find missing features or performance issues. We were positively surprised by how good the out-of-the-box performance was. We will implement a few more benchmarks and post the results once we have verified them. We also implemented multiple PRs which implement bugfixes, cleanups, and needed features like support for scalars. We also started working on LLVM optimizations which make sure that we can achieve even better performance.
    2. I noticed that our offload intrinsic allowed running Rust code on the GPU, but it wasn't of much help when calling gpu vendor libraries like cuBLAS. I implemented a new helper intrinsic which allows calling those functions conveniently, without having to manually move data to or from the device. It will benefit from the same LLVM optimizations as our full offload intrinsic. It also a bit simpler to set up on the compiler and linker side, so it already works with std and mangled kernel names, something that we still have to improve for our main offload intrinsic.
    3. A lot of work happened on the LLVM offload side for SPIRV and Intel GPU support. At the moment, our Rust frontend is tested on NVIDIA and AMD server and consumer GPUs, as well as AMD HPC and Lapotop APUs. Karol Zwolak reached out since he wants to help with with also running Rust on Intel GPUs. Offload relies on LLVM which started gaining Intel support, so hopefully we won't need much work beyond a new intel-gpu target and a new stdarch module. There is also work on a new spirv target for rustc, which we could also support if it goes through LLVM. Due to some open questions around typed pointers it does not seem clear yet whether it will, so we will have to wait.
    4. Nikita started working on updating our submodule to LLVM 22. This hopefully does not only brings some compile and runtime performance improvements, but also greatly simplifies how we can build and use offload. Once it landed I'll refactor our bootstrapping logic, and as part of that start building offload in CI.
  • Manuel Drehwaldcomment from 2026-04-01

    std::autodiff is now partly in CI, and std::offload got tested on a lot more benchmarks.

    autodiff

    Work continued on enabling autodiff in nightly. Since the last update, we have enabled autodiff in some Mingw and Linux runners. Users can now download libEnzyme artifacts, place them locally in the right spot for their toolchain, and then use autodiff on their nightly compiler. Once macOS is added, we will enable a new rustup component that will handle the download for users. Before enabling autodiff on macOS, however, we want to change how we distribute LLVM on this target (from static to dynamic linking). There are a lot of workflows and users of this target, not all of which can be modelled in the Rust CI. Our last two attempts sadly broke such downstream users and local contributors, so both attempts had to be reverted. Since testing here is tricky, progress here might be on the slower side; we will see.

    offload

    Most of the work on the offload side lately has been invisible, since we were working on implementing more benchmarks and LLVM optimizations, as well as missing features, discovered by those benchmarks. We achieved excellent performance on those benchmarks; more details will soon be presented by Marcelo Domínguez at the EuroLLVM conference in two weeks!

    Beyond benchmarks, there was a lot of tinkering on smaller PRs, reviewing, and housekeeping. LLVM-22 landed, so we updated our bootrstrap code to make use of new APIs, and tried to move a few smaller PRs forward, mainly around a better user experience and for making more Rust features available. Since the focus is still on benchmarks, not many of those PRs landed. They are in a mostly ready state, so it's a good time to pick them up if you're considering contributing. Please ping me on Zulip or in any PR with the offload label if you are interested!

Getting Rust for Linux into stable Rust: compiler features

4 detailed updates available.

Getting Rust for Linux into stable Rust: language features

6 detailed updates available.
  • Tomas Sedoviccomment from 2026-01-19

    Update from the 2026-01-14 meeting.

    Deref / Receiver

    Ding's arbitrary_self_types: Split the Autoderef chain rust#146095 is waiting on reviews. It updates the method resolution to essentially: deref_chain(T).flat_map(|U| receiver_chain(U)).

    The perf run was a wash and a carter has completed yesterday. Analysis pending.

    RFC #3851: Supertrait Auto-impl

    Ding has submitted a Rust Project goal for Supertrait Auto Impl.

    Arbitrary Self Types rust#44874

    We've discovered the #[feature(arbitrary_self_types_pointer)] feature gate. As the Lang consensus is to not support the Receiver trait on raw pointer types we're probably going to remove it (but this needs further discussion). This was a remnant from the original proposal, but the Lang has changed direction since.

    derive(CoercePointee) rust#123430

    Ding is working on a fix to prevent accidental specialization of the trait implementation. rust#149968 is adding an interim fix.

    Alice opened a Reference PR for rust#136776. There are questions around the behaviour of the as cast vs. coercions.

    Pass pointers to const in assembly rfc#3848

    Gary opened implementation for the RFC: rust#138618.

    Field Projections goal#390

    Benno updated the Field Representing Types PR to the latest design. This makes the PR much simpler.

    Tyler opened a Beyond References wiki to keep all the proposals, resources in one place.

    In-place initialization goal#395

    Ding is writing a post to describe all the open proposals including Alice's new one that she brouhght up during the LPC 2025. He'll merge it in the Beyond References wiki.

    Macros, attributes, derives, etc.

    Josh brought up his work on adding more capable declarative macros for writing attributes and derives. He's asked the Rust for Linux team for what they need to stop using proc macros.

    Miguel noted they've just added dependency on syn, but they would like to remove it some day if their could.

    Benno provided a few cases of large macros that he thought were unlikely to be replaceable by declarative-style ones. Josh suggested there may be a way and suggested an asynchronous discussion.

  • Tomas Sedoviccomment from 2026-02-16

    Updates from the 2026-01-28 and 2026-02-11 meetings:

    Removing the likely/unlikely hints in favour of cold_path

    The stabilization of core::hint::cold_path lint is imminent and after it, the likely and unlikely hints are likely (pardon the pun) to be removed.

    The team discussed the impact of this. These hints are used in C but not yet in Rust. cold_path would be sufficient, but likely/unlikely would still be more convenient in cases where there isn't an else branch. Tyler Mandry mentioned that these can be implemented in terms of cold_path.

    Niche optimizations

    We discussed the feasibility of embedding data in lower bits of a pointer -- something the kernel is doing in C. This could also enable setting the top bit in the integers (which is otherwise never set) and make it represent an error in that case (and a regular pointer otherwise).

    Ideally, this would be done in safe Rust, as the idea is to improve the safety of the C code in question.

    Extending the niches is something Rust wants to see, but it's waiting on pattern types. There are short/medium-term options by using unsafe and wrapping it in a safe macro, but the long-term hope is to have this supported in the language.

    Vendoring zerocopy

    The project has interest in vendoring zerocopy. We had its maintainers Jack Wrenn and Joshua Liebow-Feeser join us to discuss this and answer our questions. The main question was about whether to vendor at all, how often should we (or will have to) upgrade, and how much of it is expected to end up in the standard library.

    The project follows semver with the extended promise to not break minor versions even before 1.0.0. We could vendor the current 0.8 and we should be upgrade on our own terms (e.g. when we bring in new features) rather than being forced to.

    Right now, the project is able to experiment with various approaches and capabilities. Any stdlib integration a long way away, but there is interest in integrating these to the language and libraries where appropriate.

    New trait solver

    There's been a long-term effort to finish the new trait solver, which will unblock a lot of things. Niko Matsakis asked about things it's blocking for Rust for Linux.

    This is the list: unmovable types, guaranteed destructors, Type Alias Impl Trait (TAIT), Return Type Notation (RTN), const traits, const generics (over integer types), extern type.

    2026 Project goals

    This year brings in the concept of roadmaps. We now have a Rust for Linux and a few more granular Goals. We'll be adding more goals over time, but the one merged cover what we've been focusing on for now.

  • Tomas Sedoviccomment from 2026-03-11

    Update from the 2026-02-25 meeting:

    2026 Project goals

    We spent most of the meeting going over the open Project goals, the Rust for Linux roadmap and other things we'd like to see that aren't the right shape for a goal.

    Miguel Ojeda brought up the upcoming Debian 14 release (coming out probably somewhere around Q2 of 2027) and we went over each item and decided whether it's something we need to make sure is in that release or not.

    Debian stable is an important milestone and the Rust version in it serves as a baseline for Rust for Linux development.

    I'll add all this data into the roadmap.

  • Tomas Sedoviccomment from 2026-03-16

    Update from the 2026-03-11 meeting:

    Field projections

    We now have a macro and machinery that uses the projection mechanism.

    The dma_read! / dma_write! macros switched over to it. This also fixes a soundness issue 1.

    Note: this is done entirely via macros and doesn't use any Field projections language features. The Field projection syntax and traits should make this more ergonomic and integrate the borrow checker so we can accept more code.

    We're planning to have a design meeting with the Lang team in the last week of March.

    rustfmt imports formatting and trailing slashes

    We talked about the rustfmt formatting of the use statements again. While the trailing empty comment // workaround (see this update) is acceptable as a temporary measure, we need to find a long-term solution where you can configure rustfmt to accept this style.

    We don't have a issue for this specific formatting yet, though it was discussed in #3361.

    The next step are to create such an issue. We were hesitant to add burden to a team that's already at limit, but having the issue would let us track it from the Rust for Linux side.

  • Tomas Sedoviccomment from 2026-03-26

    Update from the 2026-03-26 meeting:

    Const generics

    Boxy asked the team for features that are most important under the const generics umbrella. This might help with prioritisation and just understanding of practical uses.

    1. Ability to do arithmetic on const generic types: e.g. the kernel has a type Bounded which has a value and a maximum size (in bits). Both the bit width and value are const values. They want to be able to do arithmetics on these types (starting with bit shifts) that will guarantee the the result will fit within the specified size at compile time.
    2. Argument-position const generics: right now, the const generic types must be specified in the type bound section (within the angle brackets). So for example you have to write: Bounded::<u8, 4>::new::<7>() instead of the more natural Bounded::<u8, 4>::new(7). This gets more complicated when there's const-time calculation happening rather than just a numerical constant -- in which case this also needs to be wrapped in curly brackets: { ... }.
    3. Being generic over types other than numbers: pointers would be useful for asm_const_ptr. String literals too -- even if they're just passed through without being processed / operated on. And if going from a passthrough string makes it possible to pass through any type, that would help the team replace some typestate patterns they're using with an enum.
    statx

    Alice Ryhl proposed being able to create std::fs::Metadata from Linux statx syscall.

    This was discussed in the Libs-API meeting and they had questions about possible evolutions of the statx ABI -- if/how it can grow in the future and how they could handle that if they wanted some of the new data available. So we discussed it in the Rust for Linux meeting.

    In the end, it seems prudent to be reasonably defensive rather than relying on the syscall pre-filling default values.

    Alice Ryhl proposed an opaque statx struct that would give the stdlib a way to decide on the struct's size, pre-filled contents and mask.

    Miguel Ojeda suggested contacting Christian Brauner and Alexander Viro (i.e. the VFS maintainers); Josh Triplett agreed that it would be good if we can get a thread with the right people in linux-fsdevel.

  • Tomas Sedoviccomment from 2026-04-10

    Update from the 2026-04-08 meeting:

    zerocopy features in Rust's std

    zerocopy uses two traits that are both polyfills for unstable traits : KnownLayout (for ptr_metadata) and Immutable (for Freeze). It would help maintenance of zerocopy (which Rust for Linux plans to start using) if these were stabilised.

    ptr_metadata is something the team wants in the kernel independently. It's possibly blocked on (or at least might have interactions with) the Sized Hierarchy work.

    Freeze (now NoCell) has an RFC.

    Deref/Receiver

    Jack Huey started reviewing Ding Xiang Fei's PR to split the autoderef chain and feels it's not ready to go in front of the full Lang team.

    We also discussed the dependence/independence of the Deref and Receiver implementations, in particular whether it ever makes sense to implement Deref but not Receiver. Josh Triplett suggested gathering examples for cases like that (where you can't use the type as a Self type in the function declaration, but allow calling methods on it).

    The current plan for the experiment is to have these traits separate, but have the compiler enforce that if they implement the same type, their targets are identical. This will let us open the door for any future possibilities (a supertrait / subtrait relation, or having diverging targets in the future).

    We want to experiment to see where and how these traits and their possible evolution might be helpful.

    null-ptr-deref

    The team would like to have a (an optional) compiler guarantee, that the compiler never removes null checks on raw pointers. What can currently happen in C is that if you deref a null pointer, the compiler can do optimisations including removing any subsequent checks whether that pointer is null, because dereferencing a null pointer is undefined behaviour.

    But the null check can still help prevent further bugs and in C, the kernel now disables the optimisation that would remove it.

    Miguel Ojeda is going to open an MCP for this.

    In-Place Initialization

    Benno Lossin opened a proposal for an in-person room at the 2026 All Hands for In-place initialization.

    Here's a meta issue tracking all the proposals and discussions about the feature.

    The design space is complex and the team hopes that discussing it in person will help move it forward.

Implement Open API Namespace Support

MIR move elimination

1 detailed update available.
  • Amanieu d'Antrascomment from 2026-04-03

    The RFC has just been published. It has been significantly reworked since the last draft.

    Notable changes:

    • Removed the concept of activation/de-activation. Now the semantics don't need to deal with partially allocated locals. This is less powerful optimization-wise but should still cover most cases.
    • Added byref/byval to call arguments to clarify how they are passed.
    • Added a separate section for the surface language changes to separate it from the MIR changes.
    • Added more details on the MIR optimization which eliminates moves.
    • Changed the MIR operand evaluation order to be left-to-right, except for destination places which are always evaluated last.
    • Added StorageLive back: we need it to mark the location where llvm.lifetime.start should be inserted, which is not the same as the location where a local is initialized. In the opsem, StorageLive doesn't actually allocate the local, that's still done when it is initialized by a write.

Prototype a new set of Cargo "plumbing" commands

Prototype Cargo build analysis

1 detailed update available.
  • Weihang Locomment from 2026-01-08

    The prototype of this project goal is basically complete.

    Current state

    This project goal introduces build analysis support in Cargo, with the aim of making build behavior understandable across multiple invocations, not just a single run.

    At a high level, the prototype:

    • Records build metadata over time, including:
      • rebuild reasons
      • timing information
      • relevant invocation context
    • Stores this data locally in a structured log format suitable for later analysis
    • Exposes the data via unstable cargo report subcommands, such as:
      • cargo report sessions - list session IDs
      • cargo report timings - HTML timing report
      • cargo report rebuilds - Why things rebuilt

    See the Reference for a more thorough usage documentation


    Path towards stabilization

    Before this feature can be stabilized, the following unresolved questions must be answered. They might not block stabilization, but need to be evaluated if it is fine to leave for future.

    cargo report commands

    This is a stabilization blocker.

    • Currently all three report commands (sessions, rebuilds, timings) implicitly inspect global log files when if not in a workspace.
      • Should this be explicit with a flag?
      • Should this be an error if not in a workspace?
    • Bikeshed on command names
      • Currently we have all nouns
        • For sessions
          • runs simple but ambiguous
          • Just log like git log
          • history user-friendly (docker history, shell history, though not alike)
        • For timings:
          • Not controversial, as we have --timings flag already
        • For rebuilds:
          • rebuild-reasons more explicit
      • Or move to action-oriented verbs:
      • cargo report list-sessions
      • cargo report analyze-timings (bazel analyze-profile)
      • cargo report explain-rebuilds
      • Or question-oriented verbs:
      • cargo report what-ran more general (buck2 log what-ran)
      • cargo report why-rebuilt/why-reran
    cargo report sessions
    • Currently it prints a human-readable output without a format for programmable use cases.
      • Should we provide a programmable output (for example behind --message-format=json)?
    cargo report rebuilds
    Log message schema

    This is a stabilization blocker.

    Log infrastructure

    These are mostly future possibilities, not a stabilization blocker, as it is highly possible to do incremental improvements.

    See also https://github.com/rust-lang/cargo/issues/16471#issuecomment-3724915770

    Nested Cargo calls

    See https://github.com/rust-lang/cargo/issues/16477.

    Basically, we need to have a way to associate log files of nested Cargo calls. That helps other tools as well as cargo fix itself.

    This is a stabilization blocker.

    How contributors can help

    Future contributors can help by:

    A series of follow-up tasks has been cut to track remaining work:

    • https://github.com/rust-lang/cargo/issues/16470
    • https://github.com/rust-lang/cargo/issues/16471
    • https://github.com/rust-lang/cargo/issues/16472
    • https://github.com/rust-lang/cargo/issues/16473
    • https://github.com/rust-lang/cargo/issues/16474
    • https://github.com/rust-lang/cargo/issues/16475
    • https://github.com/rust-lang/cargo/issues/16477
    • https://github.com/rust-lang/cargo/issues/16488

reflection and comptime

5 detailed updates available.

Rework Cargo Build Dir Layout

2 detailed updates available.
  • Ross Sullivancomment from 2026-01-15

    Fine grain locking for build-dir was merged and now available on nightly via -Zfine-grain-locking unstable flag. 🎉

    There are some known issues we'd like to address before doing a formal call for testing. Notably, improving blocking messages, fixing potential thread starvation in Cargo's job queue when locks block, and investigate increasing rlimits to reduce risk of hitting max file descriptors for large projects.

    I am hopeful that these issues will be resolved over the coming month and we can do a call for testing to start gathering feedback from the community on whether the new locking strategy improves workflows.

  • Ross Sullivancomment from 2026-03-09

    After the initial PR from the last update was merged, we shifted our focus to resolving some of the known issues. Notably, locking blocks the Cargo job queue slowly causing thread starvation if many build units are held by another Cargo instance.

    We investigated adding the ability for Cargo to "suspend" a job internally while waiting for a lock, but we felt this change was a bit invasive and did not fit well with how the job queue was designed. Instead we plan to change our design to acquire all build unit locks prior to running the job queue (see #16657).

    At the same time, we have continued to refine the new build-dir to prepare it for a call for testing and eventual stabilization. (#16542, #16502, #16515, #16514)

    Finally we decided to split .cargo-lock into 2 locks to allow cargo check and cargo build to run in parallel when artifact-dir == build-dir (and -Zfine-grain-locking is enabled)

    I suspect this may be the last update on this goal, as the 2026 slate of goals is coming up. While I did not renew this goal for 2026, I do plan to continue work on this and eventually stabilize this within this year.

Run more tests for GCC backend in the Rust's CI

Rust Stabilization of MemorySanitizer and ThreadSanitizer Support

3 detailed updates available.

Rust Vision Document

  • People involved: Niko Matsakis, vision team
  • Status: Partially completed; work continues outside of Project Goals

rustc-perf improvements

Stabilize public/private dependencies

Stabilize rustdoc doc_cfg feature

SVE and SME on AArch64

5 detailed updates available.
  • David Woodcomment from 2026-01-15

    rust-lang/rust#143924 has been merged, enabling scalable vector types to be defined on nightly, and I'm working on a patch to introduce unstable intrinsics/scalable vector types to std::arch

  • David Woodcomment from 2026-02-17

    Progress has been slow since the last update because I've been busy, but I've been working on a rebase of rust-lang/stdarch#1509, which has bitrot quite a bit. Rémy Rakic is joining me to work on the Sized Hierarchy parts of the goal.

  • David Woodcomment from 2026-03-17

    On the scalable vector half of the goal, I've got a branch with rust-lang/stdarch#1509 rebased, though without the intrinsic-test tool having been updated - that ended up being tricky and we've agreed to do it as a follow-up. We've opened rust-lang/rust#153286 with the compiler fixes that the stdarch patch requires, which should land soon (rust-lang/rust#153653 was opened and landed in the interim).

    On the sized hierarchy half of the goal, Rémy Rakic has been updating our RFC such that we can discuss it in design meetings with the language team on the 18th and 25th - we'll update rust-lang/rfcs#3729 later today. We've split out the const Sized parts as a future possibility (though one we are committed to pursuing) as that has more open design questions, and we've discussed the proposed syntax and approach to migration - which are what we intend to focus on in the design meetings. He's also been working out how we can start implementing our migration strategy and help resolve blockers in other areas.

  • David Woodcomment from 2026-03-17

    Per last comment, rust-lang/rfcs#3729 has been updated

  • David Woodcomment from 2026-04-14

    For the scalable vector half of the goal, we've landed a bunch of compiler fixes - rust-lang/rust#153286, rust-lang/rust#153608, rust-lang/rust#154850, rust-lang/rust#154950, rust-lang/rust#155106 and rust-lang/rust#155243 - and opened our stdarch patch with intrinsics - rust-lang/stdarch#2071. That patch should be passing CI tomorrow once nightly updates to fix an unrelated spurious CI failure. We've got a handful of follow-ups to do afterwards, listed on rust-lang/rust#145052.

    For the sized hierarchy half of the goal, Rémy Rakic and I had two design meetings with the language team (2026/03/18 and 2026/03/25) discussing the syntax/naming and migration strategy respectively.

    On syntax, the language team preferred introducing an "only bounds" syntax to control opting-out of default bounds and opting-in to alternative bounds in a family of traits (described in an alternative in the RFC), but there was an open question of whether that syntax should apply to an individual bound or all of the bounds - Niko Matsakis is investigating that.

    On naming, the language team also preferred the name SizeOfVal over MetaSized, and didn't like Pointee but had no better alternatives. Rémy Rakic prepared rust-lang/rust#154374 to do that renaming and started a discussion with the library team to confirm they were happy with the name, because changing it involves an amount of churn. The library team wanted to know what other traits in the hierarchy might later be introduced, as that would help inform the naming of the currently proposed traits, so Rémy Rakic wrote up a document with that information. We're holding off on doing any name changes until we find some consensus between libs and lang - who is responsible for these traits' names is a bit unclear.

    On migration, the language team were largely happy with our proposed approach, and we realised that the approach proposed by lcnr for associated types might also work for our other migrations. Rémy Rakic has had meetings with lcnr to better understand that approach and to work out the next steps for implementing it.

Type System Documentation

Unsafe Fields

2 detailed updates available.

Mozilla Privacy Blog

Mozilla to UK regulators: VPNs are essential privacy and security tools and should not be undermined

In the context of concerns around young people’s interactions with digital technologies, the UK’s Department for Science, Innovation and Technology is consulting on additional measures to prepare young people for growing up in a digital world. Before the backdrop of users circumventing age assurance systems mandated under the UK’s Online Safety Act, the consultation considers age-gating virtual private networks (VPNs).

Mozilla’s mission is grounded in the belief that the internet must remain open and accessible to all, and that privacy and security online are fundamental human rights. We recognize that the protection of young people online is one of the most pressing and challenging questions of our time, and we are committed to supporting policy proposals that address the root causes of online harms. We are concerned, however, that blunt interventions like mandatory age assurance and restricting access to tools like VPNs are not effective in improving the protection afforded to young people online, while undermining the fundamental rights of all users.

VPNs serve as critical privacy and security tools for users across all ages. By hiding users’ IP addresses, VPNs help protect users’ location, reduce tracking and avoid IP-based profiling. People use VPNs for lots of different reasons: to connect to their school’s or employer’s network remotely, to avoid censorship or to simply protect their privacy and security online. While being able to access VPNs is especially important for vulnerable groups like activists, dissidents or journalists, VPNs improve everyone’s baseline protection online.

Young people are particularly vulnerable to online tracking, targeted advertising, and the risks that flow from personal data being collected and processed for commercial purposes without adequate consent or transparency. In a world in which young people are interacting with digital technologies as part of their realities from young ages onward, restricting young people’s access to privacy-protecting technologies is in tension with the goal of equipping them to navigate the internet safely and competently. In order to be able to develop agency and responsible habits in engaging with digital technologies, it is crucial for young people to be introduced to best practices and key safety and privacy tools as they engage with the online world.

Rather than age-gating technologies like VPNs, we believe that regulators should address the root causes of online harm by holding platforms to account, encouraging the responsible use of parental controls and investing in digital skills and a whole of society approach to digital wellbeing.

Read our full submission to the Department for Science, Innovation and Technology.

The post Mozilla to UK regulators: VPNs are essential privacy and security tools and should not be undermined  appeared first on Open Policy & Advocacy.

Firefox Tooling Announcements

MozPhab 2.15.1 Released

Bugs resolved in Moz-Phab 2.15.1:

  • bug 2036719 moz-phab --no-stack doesn’t work as suggested?

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 651

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

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

Crate of the Week

This week's crate is cloakrs, a library and CLI tool for detecting and masking personally identifiable information.

Despite having no suggestion to work with, llogiq is content with his choice.

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.

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

502 pull requests were merged in the last week

Compiler
Library
Cargo
Clippy
Rust-Analyzer
Rust Compiler Performance Triage

This week saw a couple of PRs affecting the new trait solver, which is steadily moving forward, in particular #156139 was a massive perf. win. #156185 optimized visibility computation, resulting in up to a 8% win on the typenum crate.

Triage done by @Kobzol. Revision range: 1d72d7e8..aa31d6d8

Summary:

(instructions:u) mean range count
Regressions ❌
(primary)
0.3% [0.1%, 0.4%] 62
Regressions ❌
(secondary)
0.5% [0.1%, 1.5%] 77
Improvements ✅
(primary)
-1.7% [-8.8%, -0.2%] 18
Improvements ✅
(secondary)
-13.6% [-85.6%, -0.0%] 34
All ❌✅ (primary) -0.2% [-8.8%, 0.4%] 80

2 Regressions, 2 Improvements, 5 Mixed; 4 of them in rollups 31 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 Rust RFCs Language Reference Leadership Council

No Items entered Final Comment Period this week for Cargo, Compiler Team (MCPs only), 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
  • No New or Updated RFCs were created this week.

Upcoming Events

Rusty Events between 2026-05-13 - 2026-06-10 🦀

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

Of the last 150 merged PRs to Bun, 108 are memory-safety-adjacent — missed cleanup on an error path, use-after-free, uninitialized reads, out-of-bounds access, reentrancy. 75 of those would not compile in a language with destructors, move semantics, and a borrow checker. One in three PRs we ship is "forgot to free something on an error path."

Of the 108, ~88 are in Zig. The ~14 in C++ are mostly ref-cycles and GC-concurrency races — the residual class that survives any language. So the Zig→Rust delta is real: the Zig bugs are exactly the destructor/ownership-fixable kind, and the C++ side is already near the floor.

Without stronger compile-time guarantees, this stays a cat-and-mouse game. The proposal is to remove the largest bug class structurally rather than fix instances of it indefinitely.

Jarred Sumner on the bun github Thanks to Brian Kung 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

Mozilla Privacy Blog

Six Million Selections Later: How the DMA Is Giving People Browser Choice

At Mozilla, we’ve long believed in giving people choice and agency over their experiences online. As power in digital markets has concentrated in a small number of large companies, there have been efforts in the US, Japan, UK, India, Korea, Brazil and elsewhere to restore competition and put choice back in people’s hands.

These efforts are at various stages, but first among them was the EU’s Digital Markets Act. Over two years since obligations came into effect, the DMA is delivering for people in some key areas.

Not everywhere. Not perfectly. And not without enforcement. But browser choice is the clearest example.

Every 10 seconds, someone picks Firefox through a DMA choice screen

Operating systems like iOS, Android, Windows, and MacOS lean on pre-installed browsers, tricky default settings, and deceptive design to make it hard for people to exercise choice and to keep independent browsers from competing on a level playing field. But where the DMA has created opportunities for genuine browser choice, people are taking it.

New Mozilla data is clear: since the rules took effect, Firefox is selected through a DMA browser choice screen every 10 seconds. That adds up to more than six million Firefox selections. And people are sticking with us: retention is five times higher when people choose Firefox through a choice screen.

Academic analysis points the same way. Independent researchers compared Firefox daily active users in the EU with 43 non-EU countries. Comparing the 15 months before and after browser choice screens rolled out on iOS, they found that Firefox daily active users (DAU) were 113% higher in the EU than it would have been without the DMA. On Android, it was 12% higher. The smaller Android effect is due to the fact that Firefox usage there started from a much higher base, and the Android rollout has been more uneven than on iOS. The research also shows that the DMA’s effect is growing over time.

Browser choice on mobile is moving, but desktop is left behind

The DMA’s work isn’t done. There remains room for improvement on mobile (including making it easier to import your data to a new browser and switch with one click). However, desktop remains largely untouched – leaving roughly 310 million desktops and laptops in the EU without equivalent browser choice. For example, Windows users are subject to deceptive design tactics and are not given an active choice. Even where choice screens exist, they are not a silver bullet; ecosystem lock-in and interoperability barriers still hold back competition and innovation.

Still, the signal is clear: when people get real browser choice, they take it and select alternatives. It’s easy for gatekeepers to dismiss this as a couple of competitors benefiting. This ignores the range of challenger browsers also reporting huge growth in the EU. What’s more, it ignores the benefit to people. DMA browser choice screens are reaching different audiences. Mozilla analysis shows that women make up a significantly higher share of Firefox selections on iOS via a choice screen than organic downloads, suggesting that choice screens may successfully reach a demographic that reports lower confidence in manually changing browser defaults. The DMA’s effects are only starting to be felt and understood.

The road ahead

Effective enforcement of the obligations is the way forward. Gatekeepers continue to test and, in many instances, openly push back against the intent of the DMA provisions. This can take the form of implementation choices that limit real user uptake, delays in rolling out effective solutions, or sustained efforts to reinterpret, weaken, or roll back key provisions.

Most evidently, privacy and security arguments are often elevated in ways that risk diverting attention from whether compliance is delivering genuine choice and competition in practice. In reality, privacy, security, and effective competition can and should be designed to work hand-in-hand. They do not always have to be traded off.

Policymakers and enforcers should remain focused on outcomes: ensuring that the DMA delivers real-world competition and user choice, and resisting efforts to dilute its impact through partial compliance or narrative reframing.

Browser choice is just the start

Mozilla’s hope is that real browser choice will become the rule, rather than the exception. And that the lessons of browser choice screens will be applied to other areas of the DMA, including data portability and interoperability. Only with full compliance – including applying the existing DMA text to AI – can the full benefits of competition and innovation be brought to people in the EU.

The post Six Million Selections Later: How the DMA Is Giving People Browser Choice appeared first on Open Policy & Advocacy.

Thunderbird Blog

Community Office Hours: Contributor Spotlight on Bogomil Shopov

If you have ever used Thunderbird in Bulgarian, the subject of this month’s office hours is one of the contributors who made that possible! Office Hours hosts Heather and Monica have been lucky enough to chat with long-time localizer Bogamil Shopov at conferences like FOSDEM. Now, they’re sitting down to talk to him about how his contributor story started, and to hear the advice he has for anyone curious about being part of Thunderbird.

We’ll be back next time, checking in on Thunderbird Pro! It’s been almost a year since we sat down with members of our team making this possible. As we’ve slowly started opening up the service to our Early Birds from the waitlist, it seemed a great opportunity to learn what users can expect, now and in the future!

A Contributor Origin Story

Bogomil has been a contributor to Thunderbird from the start! Even if he didn’t like the UI in the early days, he loved our commitment to software freedom. He wanted people to have the software in their own language, and so he started localizing Thunderbird into Bulgarian. This was in the days before Pontoon, Mozilla’s localization platform, and so this wasn’t easy work! Another hurdle, which still happens, is that small language locales tend to have smaller numbers of contributors doing localizing work.

From Mentee to Mentor

Thankfully, Bogomil had unofficial mentors to help them find their way. On the localization side, he had a Bulgarian mentor who he, in turn, introduced to Mozilla and other open source projects. More experienced Mozilla contributors answered his questions as he expressed his interest in getting more involved. Bogomil points out that this need for both good onboarding docs and genuine human connection is still something all open source projects needs – and something we can keep improving!

Now Bogomil is the experienced mentor, and he’s had some key steps to helping grow the Thunderbird localizer community. He sends thank you notes to new translators and sends resources for specific computer and email terminology to new Bulgarian volunteers. He gives his mentees specific tasks to help focus their energy and excitement, and maybe even more importantly, he gives them encouragement.

This Speaker goes to 11

While Bogomil had given several public talks in Bulgarian before COVID, after 2020 he decided to try giving conference talks in English. His first talk was at Halfstack, sponsored by Mozilla, in Vienna, on what made him happy as a developer and IT person in general. He even brought a violin player on stage with him! His talk got a warm reception, and it encouraged him to keep talking about what brought him joy, whether it was using heavy metal to help him focus or his involvement with Thunderbird and open source. If you’ve ever considered giving a talk, especially about one of your passions, and hesitated, Bogomil would tell you to do it! People are more welcoming than you might think.

Tools and Advice

Bogomil still uses Pontoon for Thunderbird desktop translations, in addition to Weblate, where Thunderbird for Android and many other open source projects live. In addition to this localization platforms, he uses CoEdIt along with web-based tools that allow him to see how certain words were previously translated. And of course, he has three release channels of Thunderbird on his computer at all times, something most of the Thunderbird team understands!

From newer contributors, Bogomil has learned how language is changing and how to keep terms, like the Bulgarian word for “browser,” up to date, in addition to other feedback. His advice to new and potential volunteers is to be open to critique. This is especially true joining a project with a long history like Thunderbird, especially before wanting to change something. And he says don’t be afraid to do things and experiment, even if things go wrong.

Watch the Talk (available on PeerTube)

Resources

Desktop Thunderbird Translation docs: https://source-docs.thunderbird.net/en/latest/l10n/index.html

Desktop Thunderbird Translation on Pontoon: https://pontoon.mozilla.org/projects/thunderbird/

Thunderbird for Android Translation docs: https://docs.weblate.org/en/latest/user/translating.html

Thunderbird for Android Translation on Weblate: https://hosted.weblate.org/projects/tb-android/

The post Community Office Hours: Contributor Spotlight on Bogomil Shopov appeared first on The Thunderbird Blog.

Hacks.Mozilla.Org

Behind the Scenes Hardening Firefox with Claude Mythos Preview

Two weeks ago we announced that we had identified and fixed an unprecedented number of latent security bugs in Firefox with the help of Claude Mythos Preview and other AI models. In this post, we’ll go into more detail about how we approached this work, what we found, and advice for other projects on making good use of emerging capabilities to harden themselves against attack.

Suddenly, the bugs are very good

Just a few months ago, AI-generated security bug reports to open source projects were mostly known for being unwanted slop. Dealing with reports that look plausibly correct but are wrong imposes an asymmetric cost on project maintainers: it’s cheap and easy to prompt an LLM to find a “problem” in code, but slow and expensive to respond to it.

It is difficult to overstate how much this dynamic changed for us over a few short months. This was due to a combination of two main factors. First, the models got a lot more capable. Second, we dramatically improved our techniques for harnessing these models — steering them, scaling them, and stacking them to generate large amounts of signal and filter out the noise.

Ordinarily we keep detailed bug reports private for several months after shipping fixes and issuing security advisories, largely as a precaution to protect any users who, for whatever reason, were slow to update to the latest version of Firefox. Given the extraordinary level of interest in this topic and the urgency of action needed throughout the software ecosystem, we’ve made the calculated decision to unhide a small sample of the reports behind the fixes we recently shipped. We’ve attempted to draw them from a range of browser subsystems, but the selection process was still somewhat arbitrary. Nevertheless, we hope that the depth and diversity of these reports lends credence to our assessment of the capabilities and our calls for defenders to begin applying these techniques:

Bug ID Description
2024918 An incorrect equality check can cause the JIT to optimize away the initialization of a live WebAssembly GC struct, creating a fake-object primitive with potential arbitrary read/write in code that had undergone extensive fuzzing by internal and external researchers.
2024437 A 15-year-old bug in the <legend> element triggered by meticulous orchestration of edge cases across distant parts of the browser, including recursion stack depth limits, expando properties, and cycle collection.
2021894 Reliably exploits a race condition over IPC, allowing a compromised content process to manipulate IndexedDB refcounts in the parent to trigger a UAF and potential sandbox escape.
2022034 A raw NaN crossing an IPC boundary can masquerade as a tagged JS object pointer, turning double deserialization into a parent-process fake-object primitive for a sandbox escape.
2024653 An intricate testcase weaving through nested event loops, pagehide listeners, and garbage collection to trigger a UAF in the attribute setter for <object> elements.
2022733 Triggers a parent UAF by flooding WebTransport with thousands of certificate hashes to stretch a race condition in a refcount-heavy copy loop, and exploits that race condition over IPC from a compromised content process.
2023958 Simulates a malicious DNS server by intercepting glibc DNS function calls in order to reproduce a UDP->TCP fallback edge case, triggering a buffer over-read and parent-process stack memory leak during HTTPS RR & ECH parsing.
2025977 20-year-old XSLT bug in which reentrant key() calls cause a hash table rehash that frees its backing store while a raw entry pointer is still in use (one of several sec-high issues we fixed involving XSLT).
2027298 Patches the color picker to simulate otherwise non-automatable user selection, then uses a synchronous input event to spin a nested event loop that re-enters actor teardown and frees the callback while it is still unwinding, triggering a content process UAF.
2023817 A compromised content process could send an arbitrary wallpaper image to be decoded in the parent process, which could be paired with a hypothetical vulnerability in an image decoder to escape the sandbox. This entailed difficult-to-automate reasoning about the trust-level of inputs in the parent process.
2029813 Escapes our in-process sandboxing technology for third-party libraries (RLBox) by leveraging a gap in the verification logic used to copy values from the untrusted to the trusted side of the sandbox boundary.
2026305 Extremely small testcase that exploits the special rowspan=0 semantics in HTML tables by appending >65535 rows to bypass clamping and overflow a 16-bit layout bitfield, which went undetected for years by fuzzers.

Note that a number of these bugs are sandbox escapes, which would need to be combined with other exploits to achieve a full-chain Firefox compromise. These reports presume that the sandboxed process that renders site content has already been compromised with some separate bug, and is now running attacker-controlled machine code attempting to escalate control into the privileged parent process. When crafting a sandbox escape, the model is permitted to patch the Firefox source code, so long as the modified code is restricted to run only in the sandboxed process[1]. Such bugs are notoriously difficult to find with fuzzing, and while we’ve had some success developing new techniques to close this gap, AI analysis provides much more comprehensive coverage of this critical surface.

Just as interesting as what the models found is what they didn’t find — not because they didn’t try, but because they were unable to circumvent Firefox’s layered defenses. For example, in recent years we received several clever reports from security researchers that managed to escape the process sandbox by triggering prototype pollution in the privileged parent process. Rather than fixing these problems one-by-one, we made an architectural change to freeze these prototypes by default. While auditing logs from the harness, we saw many attempts to pursue this line of escape that were thwarted by this design. Observing such direct payoff from previous hardening work was even more rewarding than finding and fixing more bugs.

Harnessing Models to Build a Hardening Pipeline

We’ve experimented internally with LLM code audits over the past few years, with early attempts using models like GPT 4 or Sonnet 3.5 to statically analyze high risk code for vulnerabilities. These experiments showed some promise, but the high rate of false positives made them impractical to scale.

The introduction of agentic harnesses that can reliably detect security issues has completely changed this. These can find real bugs and dismiss unreproducible speculation. The key feature of such a harness is that, given the right interfaces and instructions, it can create and run reproducible test cases to dynamically test hypotheses about bugs in code. After fixing the initial set of issues that Anthropic sent to us in February, we built our own harness atop our existing fuzzing infrastructure.

We began with small-scale experiments prompting the harness to look for sandbox escapes with Claude Opus 4.6. Even with this model, we identified an impressive amount of previously-unknown vulnerabilities which required complex reasoning over multiprocess browser engine code. At first, we supervised the process in the terminal to observe the process in real-time and tune the prompts and logic. Once this was working well, we parallelized the jobs across multiple ephemeral VMs, each tasked to hunt for bugs within a specific target file and write its findings back to a bucket.

A discovery subsystem is necessary but not sufficient. In order to scale the effort, we needed to integrate it with our full security bug lifecycle: determining what to look for, where to look, and how to handle what it produces. This last part includes deduplicating against known issues, tracking bugs, triaging them, and getting fixes shipped. While the model is the core primitive powering the harness, this full pipeline is necessary to make it useful at scale.

While harnesses may be reusable across projects, this pipeline is inherently project-specific, reflecting each codebase’s semantics, tooling, and processes. Standing this up required significant iteration, with a tight feedback loop alongside the Firefox engineers who were fielding the incoming bugs.

Upgrading the Models

Once the end-to-end pipeline is in place, it’s trivial to swap in different models when they become available. Building this pipeline early helped us find a number of serious bugs using publicly-available models, and it also helped us hit the ground running when we had the opportunity to evaluate Claude Mythos Preview. In our experience, model upgrades increase the effectiveness of the entire pipeline: the system gets simultaneously better at finding potential bugs, creating proof-of-concept test cases to demonstrate them, and articulating their pathology and impact.

In addition to fixing the 271 bugs identified by Claude Mythos Preview in the 150 release, we’ve shipped more of these fixes in 149.0.2, 150.0.1, and 150.0.2. We also continue to find bugs with other means internally, and, similar to other projects, we’ve seen a significant uptick in external reports in the last few months.

Ultimately, every bug requires care and attention to properly fix. Staying on top of this unprecedented volume has led to a lot of work and long days over the last few months, and we’re extremely proud of how the team has stepped up to meet this challenge. Over 100 people contributed code to this effort to ship the most secure Firefox yet. In addition to writing and reviewing patches, others have been building and scaling this pipeline, triaging, testing the fixes, and managing the release process for each bug.

Takeaways

Anyone building software can start using a harness with a modern model to find bugs and harden their code today. We recommend getting started now. You will find bugs, and you will set yourself up to take advantage of new models as soon as they become available.

You can start with very simple prompting, then observe and iterate. Our initial prompts were not dissimilar from those described here. Through iteration we’ve built out a lot of orchestration and tooling to optimize and scale the pipeline, but the essence of the inner loop remains the same: there is a bug in this part of the code, please find it and build a testcase.

We haven’t bottomed on all the latent bugs in Firefox, but are quite pleased with the trajectory. Today, our scanning is largely focused on specific areas of the code (files, functions) where we instruct the system to look, based on a mix of human judgement and automated signals. In the near future, we intend to integrate this analysis into our continuous integration system to scan patches as they land in the tree. Models are quite flexible with the form of context provided, and we expect patch-based scanning to work as well or even better than file-based scanning.

The current moment is a perilous one, but also full of opportunity. Let’s work together to secure the internet.

 

 


FAQ

The announcement said “271 bugs”, but I count something different. What’s going on?

On the advisories web page we group all internally-reported bugs as “rollup” CVEs with multiple bugs underneath them. The web page is built from yaml in the foundation-security-advisories repo, the canonical location for our CVE assignments. While some browsers do not create CVE identifiers for internally-discovered issues at all, we provide this information in order to be as transparent as possible.

In Firefox 150, there were three internal rollups: CVE-2026-6784 (154 bugs), CVE-2026-6785 (55 bugs), and CVE-2026-6786 (107 bugs).

Astute readers will notice the number of bugs in those internal rollups adds up to 316, which is more than the 271 we announced finding with Claude Mythos Preview. That’s because our security team hunts for new bugs every day by attacking Firefox with a combination of (a) fuzzing systems (b) manual inspection and (c) this new agentic pipeline across a variety of models.

We fixed a total of 423 security bugs in releases in April. In addition to the 271 bugs announced two weeks ago, there were 41 externally reported bugs, with the remaining 111 discovered internally and split roughly in third between:

  1. Bugs found using this pipeline with Claude Mythos Preview but fixed in releases other than Firefox 150
  2. Bugs found using this pipeline with other models
  3. Bugs found with other techniques like fuzzing

Note that we also directly credited 3 CVEs to Anthropic separate from this latest effort (CVE-2026-6746, CVE-2026-6757, CVE-2026-6758). These were fixes for bugs sent to us by the outstanding Anthropic Frontier Red team a couple months ago and we assigned unique CVEs for each as per our normal process.

What do security ratings mean?

As additional context, we apply security severity ratings from critical to low to indicate the urgency of a bug:

  • sec-critical and sec-high are assigned to vulnerabilities that can be triggered with normal user behavior, like browsing to a web page. We make no technical difference between these, but sec-critical bugs are reserved for issues that are publicly disclosed or known to be exploited in the wild.
  • sec-moderate is assigned to vulnerabilities that would otherwise be rated sec-high but require unusual and complex steps from the victim.
  • sec-low is assigned to bugs that are annoying but far from causing user harm (e.g, a safe crash).

Of the 271 bugs we announced for Firefox 150: 180 were sec-high, 80 were sec-moderate, and 11 were sec-low.

While we care most about critical/high bugs, it’s normal for us to prioritize moderate and low security bugs in order to fix correctness issues and as a defense-in-depth mechanism.

Is a sec-high or sec-critical bug the same as a practical exploit?

Not necessarily.

In most cases, a single critical/high bug is not actually enough to compromise Firefox. This is because Firefox has a defense-in-depth architecture, so for example exploiting a JIT bug only achieves remote code execution in a sandboxed and site-specific process. Real-world attackers generally need to chain multiple exploits together to escalate privileges through one or more layers of sandboxing along with OS-level mitigations like ASLR.

We also generally don’t build exploits to see whether a bug could be used by an attacker in the real world. We classify sec-high based on predictable crash symptoms such as use-after-free or out-of-bounds memory issues being reported by AddressSanitizer, and our threat model assumes that any of them could be exploitable with sufficient effort. This reduces the risk of a false negative during exploitability analysis, and more importantly it allows us to focus our resources on finding and fixing more vulnerabilities.


[1] Our bug bounty program has similar rules.

The post Behind the Scenes Hardening Firefox with Claude Mythos Preview appeared first on Mozilla Hacks - the Web developer blog.

Firefox Tooling Announcements

Firefox Profiler Deployment (May 7, 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] Use custom splitter component (#4606)
  • [fatadel] Fix Download button text color when clicked (#5985)
  • [Nazım Can Altınova] Add profiler-cli for querying profiles (#5963)
  • [Nazım Can Altınova] Fix the unnecessary stringify of Uint8Array contents during zip profile extraction (#6004)

Other Changes:

  • [Samuel Glauser] Fix fullscreen icon size in bottom box (#5987)
  • [Nazım Can Altınova] Bump profiler cli version to 0.1.0 (#5996)
  • [Markus Stange] Switch from max-height to maxHeight in JSX style={{…}}. (#5990)
  • [carverdamien] Fix comment about how time and duration are stored (#5997)
  • [Nazım Can Altınova] Do not show console error when libnames are failed to parse as a URL (#5993)
  • [Nazım Can Altınova] Fix the unnecessary stringify of Uint8Array contents during zip profile extraction (#6004)

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

  • en-CA: chutten
  • en-CA: Saurabh
  • en-GB: Ian Neal
  • es-CL: ravmn
  • fy-NL: Fjoerfoks
  • ia: Melo46
  • nl: Mark Heijl
  • ru: Valery Ledovskoy
  • sv-SE: Andreas Pettersson

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

This Week In Rust

This Week in Rust 650

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
Newsletters
Project/Tooling Updates
Observations/Thoughts
Rust Walkthroughs
Miscellaneous

Crate of the Week

This week's crate is burn, a tensor and deep learning library.

Thanks to Jonas for the 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.

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

504 pull requests were merged in the last week

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

This week's result is pretty much neutral. It looks negative in icount numbers, but that's spurious, wall time remained largely unchanged. Some big performance improvements landed in the new solver, which is not enabled by default, yet.

Triage done by @panstromek. Revision range: ca9a134e..1d72d7e8

Summary:

(instructions:u) mean range count
Regressions ?
(primary)
0.6% [0.2%, 1.2%] 106
Regressions ?
(secondary)
0.7% [0.2%, 2.4%] 67
Improvements ?
(primary)
-0.6% [-1.7%, -0.2%] 66
Improvements ?
(secondary)
-0.6% [-2.8%, -0.0%] 60
All ?? (primary) 0.1% [-1.7%, 1.2%] 172

1 Regression, 2 Improvements, 9 Mixed; 5 of them in rollups 34 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 Compiler Team (MCPs only) Rust RFCs Language Team

No Items entered Final Comment Period this week for Cargo, 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-05-06 - 2026-06-03 🦀

Virtual
Africa
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

From a business standpoint, we should have reasonable confidence that it’ll stick around and be healthy for more than 10 years. We’d also like a robust ecosystem of code and tools that we can rely on, and experts we can hire.

David Anderson on the tailscale blog

Thanks to Ivan Fraixedes 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

New deploy of PerfCompare! May 5th

The latest version of PerfCompare is now live!

Check out the change-log below to see the updates:

Highlights

[kala]

Other contributions:

[kala]

[moijes]

Thank you for the contributions!

Bugs or feature requests can be filed on Bugzilla. The team can also be found on the #perfcompare channel on Element. Come and chat!

1 post - 1 participant

Read full topic

Firefox Tooling Announcements

MozPhab 2.15.0 Released

Bugs resolved in Moz-Phab 2.15.0:

  • bug 2033810 Open the browser to the uplift request form on successful moz-phab uplift
  • bug 2036007 test_integration_patch.py flaky since v2.14.0
  • bug 2036394 moz-phab: circleci => github action
  • bug 2036890 Push moz-phab to PyPI using Trusted Publisher workflow

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

1 post - 1 participant

Read full topic

Hacks.Mozilla.Org

Trustworthy JavaScript for the Open Web

The open web is a critical platform for applications that handle highly sensitive data, from private communications to financial transactions and medical records. Traditionally, servers are trusted to deliver the appropriate code and resources for their web applications to browsers, who then provide a secure and isolated environment for their execution. In some circumstances, this trust model falls short.

Consider a browser-based messaging application, like Signal or WhatsApp, which uses end-to-end encryption. The browser depends on the server to provide a trustworthy javascript implementation of the app; which ensures the user’s messages and cryptographic keys are suitably protected. A malicious or compromised server could selectively serve modified code to some users, undermining their security with little risk of detection. This challenges the basic premise of end-to-end encryption: that a misbehaving server should not be able to compromise user security.

Towards Verifiable Security on the Web

For web applications to be trustworthy in the presence of malicious servers, two properties are essential:

  • Integrity: The code executed by the user matches what the developer committed to in a manifest.
  • Transparency: These manifests are publicly logged and can be independently audited.

Web Application Integrity, Consistency and Transparency (WAICT) brings these properties to the web platform.

WAICT allows websites to cryptographically bind their client-side code to a manifest and commit that manifest to a publicly auditable log. Sites which need this stronger trust model can then opt in to WAICT enforcement. If an opted-in site delivers code that has not been publicly logged, the browser rejects it and attacks that were previously invisible become observable and attributable. This ensures that the code delivered to user’s machines is consistent with the publicly available code which security researchers can inspect.

Bringing Integrity and Transparency to the Open Web

We are collaborating with partners across the ecosystem – including Cloudflare, the Freedom of the Press Foundation and Meta – to ensure the deployment model is practical, secure, and as simple as possible. You can learn more about WAICT in our joint talk at Real World Cryptography 2026.

An early prototype of WAICT is available behind a pref in Firefox Nightly to help validate the approach in real-world scenarios. You can test drive the prototype on https://waict.dev/ – including an end-to-end encrypted video calling app secured by WAICT. The implementation is a work in progress, not a finished solution, but it provides a concrete foundation for iteration and standardization. We’re developing the specifications in the open and welcome early feedback.

WAICT marks an important step toward making strong, verifiable application security a first-class property of the open web.

With special thanks to Anna Weine, Benjamin Beurdouche, Christoph Kerschbaumer, Dennis Jackson, Frederik Braun, and Tom Schuster.

The post Trustworthy JavaScript for the Open Web appeared first on Mozilla Hacks - the Web developer blog.

Mozilla Data YouTube Channel

Last Lecture: Writing the Data Docs

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

Mozilla Privacy Blog

Mozilla calls on UK policymakers to address the roots of online harm, not undermine the open web

Mozilla has joined a coalition of 19 digital rights organizations and technology providers in a joint statement, urging UK policymakers not to undermine the open web in their efforts to protect young people online.

Our mission is grounded in the belief that the internet must remain open and accessible to all, and that privacy and security online are fundamental. Around the globe, we are witnessing blunt policy interventions like age gates or restrictions on VPNs that put these values at risk. Child safety is a complex and central issue to us all, and as we have said before, Mozilla supports robust, proportionate safeguards for minors. However, we are concerned that mandatory age verification or VPN restrictions undermine online privacy and security, people’s ability to express themselves and access information, and ultimately the health of the web itself.

In an attempt to address tough questions surrounding online harms, UK policymakers are currently consulting on which services and features should be placed behind age gates as part of a national consultation on online harms. A broad range of services are being considered for age restrictions, including search engines, games and VPNs. Even targeted age restrictions of certain features would require all users to submit to age assurance systems. However, existing age assurance technologies have been found to either undermine users’ privacy and data security, to be insufficiently accurate or not widely accessible across populations. Age restrictions could also entrench the dominance of gatekeepers and fragment the web into a patchwork of age-gated jurisdictions.

Beyond the significant risks associated with mandating age assurance across core internet services, we are particularly concerned about proposals to restrict the use of VPNs. VPNs and similar services are essential privacy and security tools used by millions of users for legitimate purposes. Restricting the use of privacy-preserving technologies undermines efforts to empower users to navigate the web safely and to develop digital literacy.

Rather than age-restricting a growing number of services, we believe that addressing the roots of child safety concerns, such as poor content moderation, irresponsible data practices, and deceptive design, is a more proportionate and effective way forward. We thus urge policymakers to prioritize policy interventions that centre children’s’ rights and all users’ agency and choice, and protect, not undermine, the open web.

The post Mozilla calls on UK policymakers to address the roots of online harm, not undermine the open web appeared first on Open Policy & Advocacy.

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

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

The Rust Programming Language Blog

Rust is participating in Outreachy

The Rust Project has been building up a good history of participating in various open-source mentorship programs, including Google Summer of Code for three years (including this year) and previously OSPP. We're happy to announce that this year we are also participating in Outreachy starting in the May 2026 cohort.

Each of these mentorship programs has different criteria for eligibility depending on who they target and the motivations of the program. Outreachy provides internships in open source, to people from any background who face underrepresentation, systemic bias, or discrimination in the technical industry where they are living. You can learn more about the Outreachy program on their website.

What is Outreachy and how is it different than Google Summer of Code

Outreachy is similar to Google Summer of Code (GSoC) in some aspects, but different in others. First off, unlike GSoC, Outreachy interns first apply to the overall program and only then can apply to specific communities. Second, while oftentimes GSoC applicants submit various contributions prior to their application, Outreachy has a dedicated period where contributions are not just optional, but required. Finally, Outreachy applicants submit an application similar to GSoC applications and communities pick interns based on those applications and the interns' contributions. Outreachy has two internship periods per year, one running from May to August (in which we are currently participating) and one from December to March.

The other major difference between Google Summer of Code and Outreachy is the source of intern stipends. For GSoC, Google graciously covers contributor stipends and overhead. For Outreachy, communities instead cover the interns' stipends and overhead.

We are mentoring 4 interns for the May 2026 cohort

Because of limited funding availability and mentoring capacity, the Rust Project decided to select four interns for mentorship. We'll briefly share these projects below.

Calling overloaded C++ functions from Rust

Ajay Singh has been selected, mentored by teor, Taylor Cramer, and Ethan Smith.

This project aims to implement an experimental feature for calling overloaded C++ functions from Rust, and to begin testing that feature in a few representative use cases.

Code coverage of the Rust compiler at scale

Akintewe Oluwasola has been selected, mentored by Jack Huey.

This project aims to develop the workflows to run and analyze code coverage of the compiler at the scale of the entire compiler test suite and on ecosystem crates detected by crater. The hope is to be able to detect when the compiler is inadequately tested, both within the compiler and in the ecosystem, and to build tools to do continuous analysis on this.

Fuzzing the a-mir-formality type system implementation

Tunde-Ajayi Olamiposi has been selected, mentored by Niko Matsakis, Rémy Rakic, and tiif.

This project aims to implement fuzzing for a-mir-formality, an in-progress model for Rust's type and trait system. The goal is to generate programs in order to identify rules with underspecified semantics in a-mir-formality.

Improve the security of GitHub Actions of the Rust Project

oghenerukevwe Sandra Idjighere has been selected, mentored by Marco Ieni and Ubiratan Soares.

This project aims to improve the security of GitHub Actions workflows of the repositories owned by the Rust Project. It will develop tools and workflows, integrating with existing software, to analyze Github repositories and detect if they follow the best security practices, fix existing issues, and ensure that good security practices are followed in the future.

What's next

Over the next 3 months, the interns will work closely with their mentors to make progress on their projects. When the internship period is over, we'll write another blog post to share the results! See you then!

We also want to thank all the people that submitted applications and made contributions. It was quite tough to decide which applicants to select. Hopefully we will participate in Outreachy again in the future and there are other opportunities to participate. We also very much welcome you to stick around and continue being involved - there is a ton of places in the Rust Project with opportunities to be involved.

William Durand

Moziversary #8

Today is my eighth Moziversary 🎂 I joined Mozilla as a full-time employee on May 1st, 2018. I previously blogged in 2019, 2020, 2021, 2022, 2023, 2024, and 2025.

You might have come across this built-in data consent thing for extensions in Firefox. I spent a good chunk of last year working on this project, from developing a technical proposal to implementing the feature in Gecko, Firefox for desktop and Firefox for Android.

Talking about Android, I became the module owner for Fenix::Add-ons, a module for all the code related to add-ons in Firefox for Android (which we call “Fenix” internally). Between the creation of this new module, and an ever-solidifying collaboration between the Add-ons and Android teams, the support for extensions in Firefox for Android has a bright future! Having started my Android journey in 2023, this feels like a noteworthy achievement.

Near the end of last year, I moved back to being a full-time AMO engineer to support a team that was down to two engineers. I redesigned the detail page, and started some refactoring on our security scanners, which I had originally created back in 2019 😬

In other news, I joined the AI/LLM/vibe-coding crowd thanks to my colleague Paul, and it took me about a month to get brain-fried… AI fatigue is real, indeed. That said, Claude code has been somewhat useful to me, and I don’t hate it, but I also don’t love it.

Thank you to everyone in the Add-ons team as well as to all the folks I had the pleasure to work with so far. Cheers!

The Rust Programming Language Blog

Raising the baseline for the `nvptx64-nvidia-cuda` target

The nvptx64-nvidia-cuda target is a compilation target for NVIDIA GPUs. When using this target, the final output is PTX. Two version choices shape that output:

  • a GPU architecture (for example, sm_70, sm_80, …), which determines which GPUs can run the PTX, and
  • a PTX ISA version, which determines which CUDA driver versions can load (and JIT-compile) the PTX.

In Rust 1.97 (scheduled for release on July 9, 2026), the baseline PTX ISA version and GPU architecture for nvptx64-nvidia-cuda will be increased. These changes affect both the Rust compiler (rustc) and related host tooling, and they make it impossible to generate PTX artifacts compatible with older GPUs and older CUDA drivers.

The new minimum supported versions will be:

  • PTX ISA 7.0 (requires a CUDA 11 driver or newer)
  • SM 7.0 (GPUs with compute capability below 7.0 are no longer supported)

Why are the requirements being changed?

Until now, Rust has supported emitting PTX for a wide range of GPU architectures and PTX ISA versions. In practice, several defects existed that could cause valid Rust code to trigger compiler crashes or miscompilations. Raising the baseline addresses these issues and enables more complete support for the remaining supported hardware.

Removing support affects users of the architectures being removed. In this case, the most recent affected GPU architectures date back to 2017 and are no longer actively supported by NVIDIA. We therefore expect the overall impact of this change to be limited.

Maintaining support for these architectures would require substantial effort. These removals let us focus development efforts on improving correctness and performance for currently supported hardware.

What happens when I update to Rust 1.97?

If you need to target a CUDA driver that does not support PTX ISA 7.0 (CUDA 10-era drivers and older), Rust 1.97 will no longer be able to generate PTX compatible with that environment. Similarly, if you need to run on GPUs with compute capability below 7.0 (for example, Maxwell or Pascal), Rust 1.97 will no longer be able to generate compatible PTX for those GPUs.

Assuming you are targeting a CUDA driver compatible with CUDA 11 or newer and using GPUs with compute capability 7.0 or newer:

  • If you do not specify -C target-cpu, the new default will be sm_70, and your build should continue to work (but will no longer be compatible with pre-Volta GPUs).
  • If you currently specify an older -C target-cpu (for example, sm_60), you will need to either:
    • remove that flag and let it default to sm_70, or
    • update it to sm_70 or a newer architecture.
  • If you already specify -C target-cpu=sm_70 (or newer), there should be no behavioral changes from this update.

For more details on building and configuring nvptx64-nvidia-cuda, see the platform support documentation.

Firefox Nightly

Import-ant Updates – These Weeks in Firefox: Issue 201

Highlights

  • Import attributes will be supported for WebExtensions, starting in Firefox 150!
    • This allows WebExtension authors to import CSS module scripts and JSON into their JavaScript modules.
    • Examples:
      • import sheet from ‘./styles.css’ assert { type: ‘css’ };
      • import schema from “./policies-schema.json” with { type: “json” };
  • The Web Serial API is now available for testing in Firefox Nightly!
  • Dharma created a new quick action for Firefox Library
    • You can test this out in Firefox Nightly 151 by typing “library” in the URL bar

Friends of the Firefox team

Resolved bugs (excluding employees)

Volunteers that fixed more than one bug

  • Chris Vander Linden
  • Chukwuka Rosemary
  • DrSeed
  • EJiro Oghenekome
  • Frédéric Wang Nélar
  • Itoro James
  • japandi
  • John Iweh
  • jonathancabera
  • Josh Aas
  • Justin Peter
  • Keji Bakare
  • kofoworola shonuyi
  • konyhéa
  • Noble Chinonso
  • Okhuomon Ajayi
  • Oluwatobi
  • Pranjali Srivastava
  • ROSHAAN
  • Sameeksha

New contributors (🌟 = first patch)

Project Updates

Add-ons / Web Extensions

Addon Manager & about:addons
  • Removed obsolete migration logic that forced distribution language packs to be reinstalled when upgrading from Firefox versions older than 67 – Bug 2000797
    • Thanks to Aloys for contributing the changes needed to cleanup this old XPIProvider migration logic
WebExtensions Framework
  • Fixed a regression where WebRTC permission popups were queued and suppressed while an extension popup was open – Bug 1982832
WebExtension APIs
  • Fixed an edge case where tabs.move would revert splitview tabs order while moving splitview tab to a new window  – Bug 2028832
  • Fixed windows API reporting window type normal instead of popup for windows opened via window.open() – Bug 2030631
    • Thanks to Brandon Lucier for contributing this small but very much appreciated fix to the windows WebExtensions API!

DevTools

WebDriver

Lint, Docs and Workflow

New Tab Page

  • You can preview Nova on the New Tab browser.newtabpage.activity-stream.nova.enabled to true, and then opening a few tabs.
    • The browser.nova.enabled pref was just introduced to turn on the Nova design tokens. That’s still very much a work in progress.

Picture-in-Picture

Search and Urlbar

Search
  • Mandy updated the search-config-v2 schema for partnerCodes (2027191)
  • Florian fixed a high frequency intermittent test failure in the search code (2009494)
  • Standard8 fixed a bug with duplicate keywords for search engines, so that we now prefer the default search engine (2024714)
Nova
  • mconley made a rounder search input for about:newtab and about:privatebrowsing (2027144)
  • Drew is continuing his work on making Nova updates to the urlbar  (2026859)
New searchbar
Urlbar
  • Dharma also fixed a quick action telemetry error (1955058)
  • James has been working on Adaptive History for Autofill Improvements (2019695, 2019695, 2021079, 2019719, 2021036, 2028730, 2021039, 2019626)
  • Gijs landed a patch so that we use aria-notify instead of A11yUtils.announce for UrlbarView’s “special” announcements (2026753)
  • Dale added a tooltip to engines in the unified search panel (2028668)
  • Drew has been working on sports suggestions (2025052)
  • James fixed a bug where persisted search was not working for Fx versions prior to 148 (2025933)

Smart Window

Storybook/Reusable Components/Acorn Design System

UX Fundamentals

  • Felt Privacy error pages now support more NSS errors instead of falling through to the legacy page. Updated introductory text for the denied-port-access error. – 2024150
  • Fixed a test in browser_aboutCertError.js that was failing on Linux opt standalone and removed the platform skip. – 2028651
  • Added clock skew detection to the Felt Privacy error pages. When a certificate error is caused by a wrong system clock, the Felt Privacy error pages now show the same dedicated clock-skew message that the legacy error pages had, and helps guide users to correct their system time. – 2025049
  • ​​Fixed misaligned bullet points in the “What can you do?” section of Felt Privacy network error pages, restoring correct visual indentation for that list. – 2028632

The Mozilla Blog

Welcoming Abigail Besdin, Mozilla’s new Chief Operating Officer

We’re delighted that Abigail Besdin has joined Mozilla as our new Chief Operating Officer.

This is an incredibly exciting time for Mozilla. Our focus is to become the world’s most trusted software company by building products that let people use the internet openly, safely, and on their terms. As technology changes rapidly, we are working to strengthen the business foundation and infrastructure that champions our mission. Delivering on that ambition takes more than great products; it demands operational rigor. Abigail will lead this effort, demonstrating how values-driven organizations can scale with discipline, speed, and trust in the AI era.

As COO, Abigail will drive company strategy and oversee Mozilla’s Core Services teams: Business Operations, Data, Infrastructure, IT, Legal, People, Security, and Strategy. These are the functions that enable us to move quickly and scale with focus. Abigail will sharpen how we plan, prioritize, and execute across the company.

Abigail brings more than 18 years of experience building and scaling high-impact platforms. She co-founded Great Jones, a venture-backed property management startup where she raised $30M, reached $10M in ARR, and led a successful acquisition by Roofstock. At Roofstock, she served as Chief of Staff to the CEO — functioning as an internal COO — where she launched new product lines, closed and integrated two acquisitions, and led the company’s strategic planning process. 

Earlier in her career, she spent six years at Skillshare, where she launched the company’s online learning platform and built its growth and content engines from the ground up.

That combination of founder’s instinct and operator’s discipline is exactly what Mozilla needs right now. Abigail will report directly to our CEO and join the executive team.

I’ve learned firsthand that ambitious product goals are only as effective as the operations underpinning them. Mozilla’s mission is as big as it gets, and I’m thrilled to lead our Core Services organization to enable rigorous, smart, and quick decision-making across the business. With a powerful execution engine, we can make sure the best of Mozilla’s mission materializes. 

Abigail Besdin, Chief Operating Officer

Abigail studied Philosophy at NYU, with a focus on Ethics and Mathematical Logic. Born and raised in New York City, she still lives there with her husband and three kids. 

Please join us in welcoming Abigail to Mozilla.

The post Welcoming Abigail Besdin, Mozilla’s new Chief Operating Officer appeared first on The Mozilla Blog.

Firefox Tooling Announcements

Firefox Profiler Deployment (April 28, 2026)

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

Highlights:

  • [fatadel] Dim non-matching nodes in the stack chart when searching (#5935)

  • [Markus Stange] Always render the CPU-usage-aware activity graph when CPU information is available (#5918)

  • [fatadel] Add CounterDisplayConfig to counters in the processed profile format (#5912)

  • [Nazım Can Altınova] Fallback to javascript highlighting in the source view as a backup (#5936)

  • [fatadel] Replace 4 counter track components with a single generic TrackCounter (#5944)

  • [Ryan Hunt] Add a fullscreen button to the bottom box (#5605)

  • [Nazım Can Altınova] Add “Include idle samples” toggle to the call tree settings (#5968)

  • [Markus Stange] Update the hovered item when panning any viewport canvas (#5903)

  • [Nazım Can Altınova] Fix loading .json.gz profiles from inside zip archives (#5959)

  • [Markus Stange] Replace symbolicator-cli with a profiler-edit node tool (#5965)

Other Changes:

  • [fatadel] Fix arrow panel appearing behind marker tooltips (#5926)

  • [fatadel] Upgrade Node.js from v22 to v24 (#5923)

  • [Markus Stange] Use createStackTableBySkippingDiscarded in focusSelf. (#5916)

  • [Markus Stange] Propagate isJS to symbolicated funcs (#5907)

  • [Nazım Can Altınova] Properly type the return value of _languageExtForPath (#5937)

  • [Nazım Can Altınova] Update typescript eslint dependencies (#5938)

  • [Markus Stange] Modernize more of the transform functions (#5934)

  • [Paul Adenot] Fix extractGeckoLogs for structured Log marker format (bug 2022540) (#5927)

  • [Nazım Can Altınova] Move some profile fetching code into a separate module. (#5939)

  • [Markus Stange] Migrate Home page animation to CSS transitions and remove react-transition-group (#5649)

  • [Nazım Can Altınova] Fix test/lint commands on Windows and fix CI (#5947)

  • [Nazım Can Altınova] Convert profile-logic/js-tracer.tsx to a ts file (#5942)

  • [Markus Stange] Remove panelLayoutGeneration (#5946)

  • [Nazım Can Altınova] Fix eslint-config-prettier silently overriding custom rules (#5955)

  • [Markus Stange] Speed up _computeCallNodeTableHierarchy by keeping siblings ordered by func (#5964)

  • [Nazım Can Altınova] Add dark mode versions of the fullscreen icons (#5972)

  • [fatadel] Use ephemeral port for esbuild’s internal dev server (#5974)

  • [carverdamien] Remove category from LongTaskMarkerPayload (#5975)

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

  • de: Ger

  • de: Michael Köhler

  • el: Jim Spentzos

  • en-GB: Ian Neal

  • es-CL: ravmn

  • fr: Théo Chevalier

  • ia: Melo46

  • it: Francesco Lodolo [:flod]

  • nl: Mark Heijl

  • pt-BR: Marcelo Ghelman

  • ru: Valery Ledovskoy

  • ru: berry

  • sv-SE: Andreas Pettersson

  • tr: Grk

  • zh-CN: Olvcpr423

  • zh-CN: wxie

  • 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

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

L10n Report: April Edition 2026

Please note some of the information provided in this report may be subject to change as we are sometimes sharing information about projects that are still in early stages and are not final yet. 

Welcome!

Are you a locale leader and want us to include new members in our upcoming reports? Contact us!

What’s new or coming up in Firefox desktop

Firefox string deadline changes

Starting with 149, some changes in developer deadlines relating to Nightly and Beta have resulted in a slight shift in string translation deadlines, giving us 2 extra days to land strings. Previously deadlines in Pontoon were set to the Sunday ahead of the final Release Candidate but going forward they will be set to a Tuesday. For example the upcoming deadline for Firefox 151 is Tuesday, May 12.

If you’re interested to see more details on upcoming Firefox releases and milestones, https://whattrainisitnow.com has all the latest details.

UI Refresh

Behind the scenes a refresh on the visual look of Firefox has been ongoing using the internal name “Nova”. You may have seen some blog reports recently on this, or perhaps have been seeing bugs in Bugzilla with this in the title. We will start seeing new strings related to these changes here and there as development work progresses, however we don’t expect a large number of string changes stemming from this work.

That being said, these updates also bring some changes in how we communicate directly to our users within Firefox. One of these changes you may have already met: our new mascot Kit. If you missed the announcement give it a read here. You may also notice a shift voice for user directed messages — with source strings becoming more Genuine, Fiery, and Playful. See this recent update in Firefox’s brand voice for more details.

Settings redesign

Localization for the update to about:settings has been going on for some time (starting early this year) and the bulk of the translation work is behind us at this point. You may see some new strings (particularly around Privacy & Security) but many of the strings are in a viewable/testable state in Nightly 152. You can check your translations and test out the redesign by typing about:config into your URL bar, proceeding past the warning message, and searching for browser.settings-redesign.enabled and setting the value to true.

What’s new or coming up in mobile

Things have been particularly busy on mobile over the past couple of months. For example, Firefox for Android saw a significant spike in April, with the number of new strings increasing to over 200 compared to fewer than 50 in March — more than eight times the typical monthly volume*.

There are two main drivers behind this increase. First, Firefox for Android is introducing a built-in VPN feature, bringing it in line with the functionality already available in Firefox. Second, both iOS and Android teams are working on a new widget for the upcoming 2026 World Cup, allowing users to follow their team directly from the browser.

Given the short turnaround time for this feature, you will notice that many strings are intentionally kept consistent across platforms — and started landing on Desktop as well. We’re also pre-landing as many strings as possible, ahead of implementation, to give localizers more time to complete translations.

* Did you know that you can track the number of new strings in a project from the Insights page in Pontoon? Check for example Firefox for Android. In the Translation activity chart, click on New source strings in the legend to display this data. Given the difference in scale, it can also help to hide other metrics to make the chart easier to read.

What’s new or coming up in Pontoon

New documentation system. Pontoon now features a brand-new, unified documentation system. This new hub brings together previously scattered resources into a single, streamlined experience, consolidating developer, localizer, and admin documentation from three separate sites into one cohesive platform. By centralizing content, the new system makes it easier to find, navigate, and maintain documentation, ensuring contributors of all roles have quick access to up-to-date and consistent guidance.

Search. You can now set default search options directly in your profile. This allows you to tailor your search without having to adjust filters each time.

The same settings are also applied when using the recently introduced global search page, which brings a major step forward in unifying localization across Mozilla by allowing users to search for strings across all projects and locales in one place. Inspired by Transvision and designed as its successor, the feature integrates deeply with Pontoon, making it easy to filter results, compare translations across languages, and jump directly into the translation workflow.

AI integration. We’ve also refined the prompt used by the LLM-powered translation feature. The goal is not to change how the feature works, but to make its output more consistent and better aligned with the context available in Pontoon. For example, the updated prompt improves how punctuation is handled, reducing variability in suggestions.

In addition, the prompt now includes more contextual data:

  • String ID.
  • Comments, including pinned comments from project managers.
  • Matches from terminology.

This additional context helps the model generate more relevant suggestions. It also represents a first step toward making LLM suggestions more useful, ahead of potential experiments with displaying them by default alongside suggestions from traditional machine translation.

New contributors. We’re also excited to welcome a group of new contributors who have started making an impact on Pontoon over the past few months. MundiaNderi, nishitmistry, dannycolin, first-afk, wassafshahzad, huseynovvusal, and Peacanduck have all contributed valuable improvements across different parts of the project, helping us move faster and improve the overall experience.

A special shoutout goes to Serah (MundiaNderi), who not only made significant contributions but also shared insights into her work in a recent blog post about enhancing comment management in Pontoon—an excellent example of the kind of collaboration and knowledge sharing we love to see in the community.

Newly published localizer facing documentation

As part of the recent documentation update for Pontoon, we’ve reorganized the content around pretranslation to make it clearer and easier to navigate. There is now a dedicated page outlining the criteria required to enable pretranslation for a locale, along with guidance on how to monitor its effectiveness over time (for example, by tracking metrics like acceptance rate or time to review). If you’re a locale manager and want to try pretranslation for your locale, you can request it directly from Pontoon.

Over the past 12 months, we also ran a limited experiment using paid translation agencies for two locales. The goal was to restore the localization level of Firefox for Android in cases where the community was inactive — situations that have since improved, with both communities now active again.
Because volunteer communities remain the foundation of Mozilla’s localization model, we wanted to be transparent about when and why this approach was used, and what it means in practice. This includes clarifying how external support fits within a community-driven ecosystem, where localizers retain ownership and responsibility for quality and direction. You can find more details in this page.

Friends of the Lion

Image by Elio Qoshi

We continue the localizer spotlight series this year.

  • Meet Oliver from China Firefox localizer, accounting student, former Minecraft translator, and Bocchi the Rock! fan He talks about starting with a single typo, why Firefox’s independence matters to him, and how the Simplified Chinese community keeps quality high with cross-review and shared responsibility.
  • Marcelo from Argentina needs no introduction to the localization communities. From Phoenix 0.3 to 24 years later, he shares how he got started, what it meant to be part of the Firefox 1.0 release, his experience as an l10n manager, and why using Mozilla products in his own language — Spanish (Argentina) — continues to motivate him.
  • What does 18 years of volunteer localization look like? From discovering Firefox and Linux out of curiosity to leading the Portuguese translation team, Cláudio from Portugal reflects on why localization is a form of digital activism, and how every translated word helps build a more inclusive internet.
  • Baurzhan from Kazakhstan began his localization journey with a simple question: why wasn’t Kazakh available in widely used software? That curiosity grew into a long-term commitment to localization, leading to the successful translation of Firefox and many other open source projects. His work demonstrates the power of perseverance in making technology accessible to all.

If you enjoy the series, please help us identify the localizers you’d like to see featured filling out this nomination form. If you have stories to share, tell us in your own words.

Know someone in your l10n community who’s been doing a great job and should appear here? Contact us and we’ll make sure they get a shout-out!

Useful Links

Questions? Want to get involved?

If you want to get involved, or have any question about l10n, reach out to:

Did you enjoy reading this report? Let us know how we can improve it.

The Servo Blog

March in Servo: keyboard navigation, better debugging, FreeBSD support, and more!

Servo 0.1.0 represents Servo’s biggest month ever, with a record 530 commits and our first ever release on crates.io! For security fixes, see § Security.

With this release Servo becomes more accessible, thanks to tab navigation (@mrobinson, @Loirooriol, #42952, #43019, #43058, #43246, #43267, #43067), keyboard navigation with Alt+Shift and the accesskey attribute (@mrobinson, #43031, #43144, #43434), and keyboard scrolling with Space and Shift+Space (@mrobinson, #43322).

We’ve shipped several new web platform features:

Plus a bunch of new DOM APIs:

servoshell is now installed as servoshell or servoshell.exe, rather than servo or servo.exe (@jschwe, @mrobinson, #42958). --userscripts has been removed for now, but anyone who uses it is welcome to reinstate it as a wrapper around User­Content­Manager::add­_script (@jschwe, #43573). We’ve fixed a bug where link hover status lines are sometimes not legible (@simartin, #43320), and we’re working on getting servoshell signed for macOS to avoid getting blocked by Gatekeeper (@jschwe, #42912).

After a long effort by @valpackett, @dlrobertson, and more recently @nortti0 and @sagudev (#43116, #43134), we can now build Servo for FreeBSD! Note that Servo 0.1.0 still has some issues that need to be worked around, but you can get all the details in #44601.

A great deal of work went into making the crates.io release possible, including renaming libservo to just servo (@jschwe, #43141), making each package self-contained (@jschwe, #43180, #43165), fixing build issues (@delan, @jschwe, #43170, #43458, #43463) and crates.io compliance issues (@jschwe, #43459), configuring package metadata (@jschwe, @StaySafe020, #43078, #43264, #43451, #43457, #43654), and organising our dependency tree (@jschwe, @yezhizhen, @webbeef, @mrobinson, #42916, #43243, #43263, #43516, #43526, #43552, #43615, #43622, #43273, #43092). As a result, you can now take your first step towards embedding Servo in a Rust app with:

$ cargo add servo

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

Security

crypto.subtle.deriveBits() for X25519 checking for all-zero secrets, and verify() for HMAC comparing signatures, are now done in constant time (@kkoyung, #43775, #43773).

‘Content-Security-Policy’ now handles redirects correctly (@TimvdLippe, #43438), and sends violation reports with the correct blockedURI and referrer (@TimvdLippe, #43367, #43645, #43483). The policy in <meta> now combines with the policy sent in HTTP headers, rather than overriding it (@TimvdLippe, @elomscansio, #43063). When checking nonces, we now reject elements with duplicate attributes (@dyegoaurelio, #43216).

The document containing an <iframe> can no longer access the contents of error pages (@TimvdLippe, #43539), and CSP violations inside an <iframe> are now correctly reported (@TimvdLippe, #43652).

Work in progress

We’ve landed more work towards supporting IndexedDB, under --pref dom­_indexeddb­_enabled (@arihant2math, @gterzian, @Taym95, @jerensl, #42139, #42727, #43096, #43041, #42451, #43721, #43754, #42786), and towards supporting IntersectionObserver, under --pref dom­_intersection­_observer­_enabled (@stevennovaryo, @mrobinson, #42251).

We’re continuing to implement document.execCommand() for rich text editing (@TimvdLippe, #43177), under --pref dom­_exec­_command­_enabled. ‘beforeinput’ and ‘input’ events are now fired when executing supported and enabled commands (@TimvdLippe, #43087), the ‘defaultParagraphSeparator’ and ‘styleWithCSS’ commands are now supported (@TimvdLippe, #43028), and the ‘delete’ command is partially supported (@TimvdLippe, #43016, #43082).

We’re also working on the Font Loading API (@simonwuelker, #43286), under --pref dom­_fontface­_enabled. new FontFace() now accepts ArrayBuffer in its source argument (@simonwuelker, #43281).

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

Work on accessibility support for web contents continues under --pref accessibility­_enabled. There was a breaking change in the embedding API (@delan, @alice, #43029), and we’ve landed support for “grafting” the accessibility tree of a document into that of its containing webview (@delan, @alice, #43012, #43013, #43556). As a result, when you navigate, separate documents can have separate accessibility trees without complicating the embedder.

<link rel=modulepreload> is now partially supported (@Gae24, #42964), though recursive fetching of descendants is gated by --pref dom­_allow­_preloading­_module­_descendants (@Gae24, #43353).

For a long time, Servo has had some support for the Web Bluetooth API under --pref dom­_bluetooth­_enabled. We’ve recently reworked our implementation to adopt btleplug, the cross-platform Rust-native Bluetooth LE library (@webbeef, #43529, #43581).

We’re now implementing the Web Animations API, starting with AnimationTimeline and DocumentTimeline (@mrobinson, #43711).

We’ve landed more fixes to Servo’s async parser (@simonwuelker, #42930, #42959), under --pref dom­_servoparser­_async­_html­_tokenizer­_enabled. If we can get the feature working more reliably (#37418), it could halve the energy Servo spends on parsing, lower latency for pages that don’t use document.write(), and even improve the html5ever API for the ecosystem.

For developers

Servo’s DevTools feature now has partial support for inspecting service workers (@CynthiaOketch, #43659), as well as using the navigation controls along the top of the UI (@brentschroeter, @eerii, #43026).

In the Inspector tab, we’ve fixed a bug where the UI stops updating when navigating to a new page (@brentschroeter, #43153).

In the Console tab, you can now evaluate JavaScript in web workers and service workers (@SharanRP, #43361, #43492).

In the Debugger tab, you can now Step In, Step Out, and Step Over (@eerii, @atbrakhi, #42907, #43040, #43042, #43135). We’ve landed partial support for the Scopes panel (@eerii, @atbrakhi, #43166, #43167, #43232), the Call stack panel (@atbrakhi, @eerii, #43015, #43039), and showing you information when hovering over objects, arrays, functions, and other values (@atbrakhi, @eerii, #43319, #43356, #43456, #42996, #42936, #42994).

We’ve fixed some long-outstanding bugs where the DevTools UI may stop responding due to protocol desyncs (@brentschroeter, @eerii, #43230, #43236), or due to messages from multiple Servo threads being interleaved (@brentschroeter, @eerii, #43472).

For developers of Servo itself, mach can be a bit opaque at times. To make mach more transparent and composable, we’ve added mach print-env and mach exec commands (@jschwe, #42888).

We’re also working on a new dev container, which will provide an alternative to our usual procedures for setting up a Servo build environment (@jschwe, @sagudev, #43127, #43131, #43139).

Embedding and automation

Breaking changes:

Removed from our API:

You can now read and write cookies with SiteDataManager::cookies­_for­_url() and set­_cookie­_for­_url() (@longvatrong111, #43600).

ClipboardDelegate and StringRequest are now exposed to the public API, allowing you to implement custom clipboard delegates (@jdm, @chrisduerr, #43203, #43261). You can pass your custom delegate to WebViewBuilder::clipboard­_delegate().

You can now get the EmbedderControlId associated with an InputMethodControl by calling InputMethodControl::id() (@chrisduerr, #43248).

PixelFormat now implements Debug (@chrisduerr, @mrobinson, #43249).

We’ve improved the docs for Servo, ServoBuilder, WebViewBuilder, RenderingContext (@chrisduerr, #43229), EmbedderControlId, EmbedderControlRequest, EmbedderControlResponse, SimpleDialogRequest, AlertResponse, ConfirmResponse, PromptResponse, EmbedderMsg (@mukilan, #43564), ResourceReaderMethods (@jschwe, @mrobinson, #43769), servo::input­_events (@mukilan, #43681), and WheelDelta (@yezhizhen, @mrobinson, #43210).

We fixed a deadlock in WebDriver that occurs under heavy use of actions from multiple input sources (@yezhizhen, #43202, #43169, #43262, #43275, #43301), ‘pointerMove’ actions with a ‘duration’ are now smoothly interpolated (@yezhizhen, #42946, #43076).

Add Cookie is now more conformant (@yezhizhen, #43690), which led to Servo developers landing a spec patch. ‘pause’ actions are now slightly more efficient (@yezhizhen, #43014), and we’ve fixed a bug where ‘wheel’ actions fail to interleave with other actions (@yezhizhen, #43126).

More on the web platform

Carets now blink in text fields (@mrobinson, #43128). You can configure or disable blinking carets with --pref editing_caret_blink_time=0 or a duration in milliseconds. Clicking to move the caret is more forgiving now (@mrobinson, #43238), and moving the caret by a word at a time is more conventional on Windows and Linux, with Ctrl instead of Alt (@mrobinson, #43436). We’ve also fixed a bug where pressing the arrow keys in text fields both moves the caret (good) and scrolls the page (bad), and fixed a bug where the caret fails to render on empty lines (@mrobinson, @freyacodes, #43247, #42218).

Input has improved, with more responsive touchpad scrolling on Linux (@mrobinson, @chrisduerr, #43350). Pointer events and mouse events can now be captured across shadow DOM boundaries (@simonwuelker, #42987), and we’ve now started working towards shadow-DOM-compatible focus (@mrobinson, #43811). Pressing Space or Enter inside text fields no longer causes them to be clicked (@mrobinson, #43343).

The lang attribute is now taken into account when shaping, which is important for the correct rendering of Chinese and Japanese text (@RichardTjokroutomo, @mrobinson, #43447). ‘font-weight’ is now matched more accurately when no available font is an exact match (@shubhamg13, #43125).

Navigation is one of the most complicated parts of HTML: navigating can run some JavaScript that replaces the page, just run some JavaScript, or depending on the response, do nothing at all. <iframe> makes navigation doubly complicated: the document containing an <iframe> can observe and interact with the document inside the <iframe> in various ways, often synchronously. This has been the source of many bugs over the years, but we’ve recently fixed one of those major issues (@jdm, #43496).

javascript: URLs are a massive special case with many quirks, and <iframe> has its own big edge cases.

new Worker() now supports JS modules (@pylbrecht, @Gae24, #40365), and CanvasRenderingContext2D now supports drawing text with Variation Selectors, allowing you to control things like emoji presentation and CJK shaping (@yezhizhen, #43449).

Servo now fires ‘pointerover’, ‘pointerout’, ‘pointerenter’, and ‘pointerleave’ events on web content (@webbeef, #42736), ‘scroll’ events on VisualViewport (@stevennovaryo, #42771), and ‘scrollend’ events on Document, Element, and VisualViewport (@abdelrahman1234567, @mrobinson, #38773). We also fire ‘error’ events when event handler attributes contain syntax errors (@simonwuelker, #43178).

We’ve improved the default appearance of <summary> (@Loirooriol, #43111), <select> (@lukewarlow, #43175), <input type=file> (@lukewarlow, @AlexVasiluta, @lukewarlow, #43498, #43186), and <textarea> and <input type=text> and friends (@mrobinson, #43132), plus ‘::marker’ in mixed LTR/RTL content (@Loirooriol, #43201). <select> also now requires user interaction to open the picker (@SharanRP, #43485).

<form action>, <iframe src>, open(url) on XMLHttpRequest, new EventSource(url), and new Worker(url) now correctly resolve the URL with the page encoding (@SharanRP, @jdm, @jayant911, @Veercodeprog, @sabbCodes, #43521, #43554, #43572, #43537, #43634, #43588).

‘direction’ now works on grid containers (@nicoburns, #42118), SVG images can now be used in ‘border-image’ (@shubhamg13, #42566), ‘linear-gradient()’ now dithers to reduce banding (@Messi002, #43603), ‘letter-spacing’ no longer applies to invisible zero-width formatting characters (@simonwuelker, #42961), and ‘:active’ now matches disabled or non-focusable elements too, as long as they are being clicked (@webbeef, #42935).

DOMContentLoaded timings in Performance­Navigation­Timing are more accurate (@simonwuelker, #43151). Performance­Paint­Timing and Largest­Contentful­Paint are more accurate too, taking <iframe> into account (@shubhamg13, #42149), and checking for and ignoring things like broken images and transparent backgrounds (@shubhamg13, #42833, #42975, #43475).

We’ve improved the conformance of JS modules (@Gae24, #43585), <button command> (@lukewarlow, #42883), <font size> (@shubhamg13, #43103), <link media> and <link type> (@TimvdLippe, #43043), <option selected> (@SharanRP, #43582), <script integrity> and <style integrity> (@Gae24, #42931), EventSource (@mishop-15, #42179), SubtleCrypto (@kkoyung, #42984, #43315, #43533, #43519), Worker (@simonwuelker, #43329), HTMLVideoElement (@shubhamg13, #43341), dataset on Element (@TimvdLippe, #43046), and querySelector() and querySelectorAll() (@simonwuelker, #42991).

We’ve fixed bugs related to error reporting (@simonwuelker, @xZaisk, @yezhizhen, @eyupcanakman, #43191, #43323, #43101, #43560), event loops (@jayant911, #43523), focus (@jakubadamw, #43431), quirks mode (@mrobinson, @Loirooriol, @lukewarlow, #42960, #43368), <iframe> (@TimvdLippe, @jdm, #43539, #43732), the ‘animationstart’ and ‘animationend’ events (@simonwuelker, #43454), the ‘touchmove’ event (@yezhizhen, #42926), CanvasRenderingContext2D (@simonwuelker, #43218), Worker (@bruno-j-nicoletti, #43213), ‘:active’ on <input> (@mrobinson, #43722), ‘overflow: scroll’ on ‘::before’ and ‘::after’ (@stevennovaryo, #43231), ‘position: absolute’ (@yoursanonymous, @Loirooriol, #43084), and <img> and <svg> without width or height attributes (@Loirooriol, #42666). Fixing that last bug led to Servo developers finding two spec issues!

We’ve landed partial support for using CSS counters in ‘list-style-type’ on ‘display: list-item’ and ‘content’ on ‘::marker’, but the counter values themselves are not calculated yet, so all list items still read as 0. or similar. In any case, you can use a <counter-style-name> or ‘symbols()’ in ‘list-style-type’, and ‘counter()’ and ‘counters()’ in ‘content’ (@Loirooriol, #43111).

We’ve also landed partial support for <marquee> and the HTMLMarqueeElement interface, including basic layout, but the contents are not animated yet (@mrobinson, @lukewarlow, #43520, #43610).

Servo now exposes several attributes that have no direct effect, but are needed for web compatibility (@lukewarlow, #43500, #43499, #43502, #43518):

  • noHref on HTMLAreaElement
  • hreflang, type, charset on HTMLAnchorElement
  • useMap on HTMLInputElement and HTMLObjectElement
  • longDesc on HTMLIFrameElement and HTMLFrameElement

Performance and stability

We’ve fixed sluggish scrolling on long documents like this page on docs.rs (@webbeef, @yezhizhen, #43074, #43138), and reduced the memory usage of BoxFragment by 10% (@stevennovaryo, #43056). about:memory now has a Force GC button (@webbeef, #42798), and no longer reports all processes as content processes in multiprocess mode (@webbeef, #42923).

Web fonts are no longer fetched more than once, and they no longer cause reflow when they fail to load (@minghuaw, #43382, #43595). We’re also working towards better caching for shaping results (@mrobinson, @lukewarlow, @Loirooriol, #43653). Event handler attribute lookup is more efficient now (@Narfinger, #43337), and we’ve made DOM tree walking more efficient in many cases (@Narfinger, #42781, #42978, #43476).

crypto.subtle.encrypt(), decrypt(), sign(), verify(), digest(), importKey(), unwrapKey(), decapsulateKey(), and decapsulateBits() are more efficient now (@kkoyung, #42927), thanks to a recent spec update.

More of Servo now uses cheaper crossbeam channels instead of IPC channels, unless Servo is running in multiprocess mode, or avoids IPC altogether (@Narfinger, @jschwe, @Taym95, #42077, #43309, #42966). We’ve also reduced clones, allocations, conversions, comparisons, and borrow checks in many parts of Servo (@simonwuelker, @kkoyung, @mrobinson, @Narfinger, @yezhizhen, @TG199, #43212, #43055, #43066, #43304, #43452, #43717, #43780, #43088, #43226).

DOM data structures (#[dom_struct]) can refer to one another, with the help of garbage collection. But when DOM objects are being destroyed, those references can become invalid for a brief moment, depending on the order the GC finalizers run in. This can be unsound if those references are accessed, which is a very easy mistake to make if the type has an impl Drop. To help prevent that class of bug, we’re reworking our DOM types so that none of them have #[dom_struct] and impl Drop at the same time (@willypuzzle, #42937, #42982, #43018, #43071, #43222, #43288, #43544, #43563, #43631).

We’ve fixed a crash caused by an IPC resource leak when making many requests over time (@yezhizhen, #43381), and some bugs found by ThreadSanitizer and --debug-mozjs (@jdm, @Loirooriol, #42976, #42963, #43487). We’ve also fixed crashes in CanvasRenderingContext2D (@yezhizhen, #43449), Crypto (@rogerkorantenng, #43501), devtools (@simonwuelker, #43133), event handler attributes (@simonwuelker, #43178), Promise (@Narfinger, @jdm, #43470), and WebDriver (@Tarmil, @yezhizhen, #42739, #43381).

We’ve continued our long-running effort to use the Rust type system to make certain kinds of dynamic borrow failures impossible (@Narfinger, @Gae24, @Uiniel, @TimvdLippe, @yezhizhen, @sagudev, @PuercoPop, @pylbrecht, @arabson99, @jayant911, #42957, #43108, #43130, #43215, #43183, #43219, #43245, #43220, #43252, #43268, #43184, #43277, #43278, #43284, #43302, #43312, #43348, #43327, #43362, #43365, #43383, #43432, #43259, #43439, #43473, #43481, #43480, #43479, #43525, #43535, #43543, #43549, #43570, #43571, #43569, #43579, #43584, #43657, #43713).

Thanks to a wide range of people, many of whom were contributing to Servo for their first time, we’ve also landed a bunch of architectural improvements (@elomscansio, @mukilan, #43646), cleanups (@simartin, @SharanRP, @TG199, @sabbCodes, @niyabits, @eerii, @atbrakhi, #43276, #43285, #43532, #43778, #43771, #43566, #43567, #43587, #43140, #43316), and refactors (@sabbCodes, @arabson99, @jayant911, @StaySafe020, @saydmateen, @eerii, @TimvdLippe, @elomscansio, @CynthiaOketch, #43614, #43641, #43619, #43642, #43623, #43656, #43644, #43672, #43664, #43676, #43684, #43679, #43678, #43655, #43675, #43731, #43729, #43728, #43740, #43751, #43748, #43747, #43752, #43745, #43724, #43723, #43765, #43767, #43181, #43269, #43270, #43279, #43437, #43597, #43607, #43602, #43616, #43609, #43612, #43647, #43651, #43662, #43714, #43774).

Donations

Thanks again for your generous support! We are now receiving 7167 USD/month (+2.6% from February) 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 37 GitHub users (+5 from February) 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.

7167 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.

The Rust Programming Language Blog

Announcing Google Summer of Code 2026 selected projects

As previously announced, the Rust Project is participating in Google Summer of Code (GSoC) 2026. GSoC is a global program organized by Google that is designed to bring new contributors to the world of open source.

A few months ago, we published a list of GSoC project ideas, and started discussing these projects with potential GSoC applicants on our Zulip. We had many interesting discussions with the potential contributors, and even saw some of them making non-trivial contributions to various Rust Project repositories before GSoC officially started!

The applicants prepared and submitted their project proposals by the end of March. This year, we received 96 proposals, which is a 50% increase from last year. We are glad that there was again a lot of interest in our projects! Like many other GSoC organizations this year, we somewhat struggled with some AI-generated proposals and low-quality contributions generated using AI agents, but it stayed manageable.

GSoC requires us to produce an ordered list of the best proposals, which is always challenging, as Rust is a big project with many priorities. Our mentors examined the submitted proposals and evaluated them based on their prior interactions with the given applicant, their contributions so far, the quality of the proposal itself, but also the importance of the proposed project for the Rust Project and its wider community. We also had to take mentor bandwidth and availability into account. Unfortunately, we had to cancel some projects due to several mentors losing their funding for Rust work in the past few weeks.

As is usual in GSoC, even though some project topics received multiple proposals1, we had to pick only one proposal per project topic. We also had to choose between proposals targeting different work to avoid overloading a single mentor with multiple projects. In the end, we narrowed the list down to the best proposals that we could still realistically support with our available mentor pool. We submitted this list and eagerly awaited how many of them would be accepted into GSoC.

Selected projects

On the 30th of April, Google has announced the accepted projects. We are happy to share that 13 Rust Project proposals were accepted by Google for Google Summer of Code 2026. That is a lot of projects! We are really happy and excited about GSoC 2026!

Below you can find the list of accepted proposals (in alphabetical order), along with the names of their authors and the assigned mentor(s):

Congratulations to all applicants whose project was selected! Our mentors are looking forward to working with you on these exciting projects to improve the Rust ecosystem. You can expect to hear from us soon, so that we can start coordinating the work on your GSoC projects.

We are excited to mentor three contributors who already experienced GSoC with us in the previous year. Welcome back, Kei, Marcelo and Shourya!

We would like to thank all the applicants whose proposal was sadly not accepted, for their interactions with the Rust community and contributions to various Rust projects. There were some great proposals that did not make the cut, in large part because of limited mentorship capacity. However, even if your proposal was not accepted, we would be happy if you would consider contributing to the projects that got you interested, even outside GSoC! Our project idea list is still current and could serve as a general entry point for contributors that would like to work on projects that would help the Rust Project and the Rust ecosystem. Some of the Rust Project Goals are also looking for help.

There is a good chance we'll participate in GSoC next year as well (though we can't promise anything at this moment), so we hope to receive your proposals again in the future!

The accepted GSoC projects will run for several months. After GSoC 2026 finishes (in autumn of 2026), we will publish a blog post in which we will summarize the outcome of the accepted projects.

Firefox Tooling Announcements

MozPhab 2.14.0 Released

Bugs resolved in Moz-Phab 2.14.0:

  • bug 2032102 Parallelize revision creation and diff property calls in submit for faster stack submission

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

1 post - 1 participant

Read full topic

Mozilla Data YouTube Channel

Glean Dictionary Looker Demo

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

Firefox Tooling Announcements

MozPhab 2.13.1 Released

Bugs resolved in Moz-Phab 2.13.1:

  • bug 2033054 Add AGENTS.md/CLAUDE.md for moz-phab
  • bug 2034269 reorg --force aborts on abandoned-revision ghost links in stackGraph

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

1 post - 1 participant

Read full topic

Jonathan Almeida

Rebase all WIPs to the latest upstream head

A small pet-peeve with fetching the latest main on jujutsu is that I like to move all my WIP patches to the new one. That's also nice because jj doesn't make me fix the conflicts immediately!

The solution from a co-worker (kudos to skippyhammond!) is to query all immediate decendants of the previous main after the fetch.

jj git fetch
# assuming 'z' is the rev-id of the previous main.
jj rebase -s "mutable()&z+" -d main

I haven't learnt how to make aliases accept params with it yet, so this will have to do for now.

Update: After a bit of searching, it seems that today this is only possible by wrapping it in a shell script. Based on the examples in the jj documentation an alias would look like this:

Update 2: After some months of usage across multiple repositories, I've found it better to be clear with the destination since main, trunk or others can be tracked with a combination of repository aliases too.

[aliases]
# Update all revs to the latest main; point to the previous one.
hoist = ["util", "exec", "--", "bash", "-c", """
set -euo pipefail
jj rebase -s "mutable()&$1+" -d "$2"
""", ""]

You can use this to rebase all your WIPs like so:

$ jj hoist <prev_main> <current_main>

If my previous main revision was kz, this is what I would end up doing:

$ jj fetch origin
$ jj hoist kz main@origin

Thunderbird Blog

Thunderbird Pro April 2026 Update

One of the most exciting aspects of bringing Thunderbird Pro to life is the opportunity to build an email service from Thunderbird together with our community, giving users the control and freedom they expect without relying on third party email service providers.

Over the past few months, we’ve been checking in with our community through quick surveys, and the feedback is clear: people care most about Thundermail. We’re listening and working to deliver what you expect as quickly as possible, focusing our resources on building a great Thundermail experience first, with Appointment and Send as power features alongside that foundation. We’re also adjusting the initial price to better align with your expectations.

We’ll be sending out the first wave of Early Bird Beta invites next month. If you haven’t already, please join the waitlist HERE and keep an eye on your inbox. We’re excited to get Thundermail into your hands and continue building it together.

Latest Thundermail Developments

Our work right now is focused on making Thundermail reliable, easy to set up, and ensuring a smooth onboarding experience with an intuitive design, both visually and functionally.

Sign-in and Setup

A new connection flow is in development that will make it much easier to add a Thundermail account to Thunderbird, including options like QR code setup and deeper integration within the app. We have also fixed a range of sign in issues, improved domain setup, and made it easier to move from account creation to actually using the service.

The account dashboard has been updated for a cleaner look, smoother onboarding, and easier access to the key details our users care about.  Configuring settings like app passwords, custom domains and aliases are now front and center when you first sign in.

Infrastructure

On the infrastructure side, we’re continuing to improve stability and performance. This includes completed work on upgrading Stalwart to strengthen spam detection so legitimate emails are far less likely to end up in spam, along with improvements to how we monitor the services so problems are easier to catch and less likely to affect users. Everyday actions like archiving and managing settings should feel more intuitive for users, and the web app, add-ons, and related services now work together more smoothly.

April Onward

  • Next up for the account experience is better alias and custom-domain handling, and even better integration between Thunderbird and the web account flow.
  • The dashboard is also getting another round of refinement so settings, account details, and subscription information are easier to understand at a glance.
  • Thundermail work continues by focusing on reliability and security, including aliases, delivery, transport security, and admin access controls.
  • There will also be a final layer of polish across the entire experience between the web app, add-on, and desktop flows.
  • Finally: Webmail is moving up our priority list. While still early, development is actively progressing and we’re aiming to bring a usable experience much sooner than originally planned.

Progress on Appointment and Send

While Thundermail is our primary focus, work on other Thunderbird Pro services is continuing.

For Appointment, we’ve made progress on reliability and backend performance, including improvements to how calendar tasks are processed and fixes to event handling. Our priorities heading up to the release are also focused on reliability, with refinement on calendar connections, event syncing, Zoom access, and a simpler first-time setup flow.

For Send, we’ve made substantial visual improvement so that it feels like a more natural part of Thunderbird Pro. We’ve also made a number of security improvements and are continuing to evaluate infrastructure choices to ensure long term reliability. Our priorities for Send in the coming months include better encryption-key handling and clearer password-protected downloads.

What’s Next

We’ll begin inviting people from the waitlist into the Early Bird beta shortly. If you haven’t signed up yet, now’s the time. Your feedback will directly shape how Thundermail evolves.


For more up to date news, check out our services roadmap at: https://roadmaps.thunderbird.net/services/

If you want to get involved in the direction of these features or want to contribute ideas to the team, you can visit https://ideas.tb.pro/.

The post Thunderbird Pro April 2026 Update appeared first on The Thunderbird Blog.

Firefox Nightly

VPN, Split View, and Other Goodies – These Weeks in Firefox: Issue 200!

Highlights

Friends of the Firefox team

Resolved bugs (excluding employees)

Script to find new contributors from bug list

Volunteers that fixed more than one bug

  • Chris Vander Linden
  • EJiro Oghenekome
  • Keji Bakare
  • konyhéa
  • Noble Chinonso
  • Pranjali Srivastava
  • Sameeksha

New contributors (🌟 = first patch)

Project Updates

Add-ons / Web Extensions

Addon Manager & about:addons
  • Fixed a long-standing issue where extension paths stored in extensions.json (and addonStartup.json.lz4) became incorrect after restoring a Firefox profile to a different location and causing all previously installed add-ons to fail to load  – Bug 1429838

DevTools

WebDriver

Lint, Docs and Workflow

New Tab Page

Search and Urlbar

  • Dao and Moritz worked on follow ups and fixed several bugs related to the new search bar implementation, which shipped in Fx 149 (2026248, 2023611, 2025746, 2022159, 2022809, 2023656, 2023141)
  • Mandy added a new `hasBeenUsed` attribute to search engines to enable better messaging system targeting (2024078)
  • Mandy also added a new telemetry category for when search mode is activated via a feature callout (2018806)
  • Drew and Daisuke are continuing their work on making Nova updates to the urlbar (2019165)
  • Dale fixed two bugs related to the new Unified Trust Panel (2019928, 2013044)
  • Florian used Claude to fix intermittent bugs related to urlbar and search telemetry (2009767, 2024301)
  • James fixed a high frequency intermittent test failure in the urlbar code (2001962)
  • Marco fixed a high frequency intermittent test failure in the places database code (1981199)

Tab Groups

DJ is adding the ability to copy all URLs from the tabs in a tab group 1984338 – Add a way to share/send/copy all tabs urls from a given tab group

Mozilla Addons Blog

WebExtensions API Changes (Firefox 149-152)

Intro

Hey everyone, we’ve been working on some exciting changes, and want to share them with you.

But first, let me introduce myself. I am Christos, the new Sr. Developer Relations engineer in Add-ons, and I’m excited to write my first post on the Add-ons engineering blog.

Deprecations and changes

To start, I’m looking at a couple of features that are going away: avoiding content script execution in extension contexts, decoupling file access from host permissions, and improving the display of pageAction SVG icon.

executeScript / registerContentScript in moz-extension documents

Deprecated: Firefox 149  Removed: Firefox 152

Starting in Firefox Nightly 149 and scheduled for Firefox 152, the scripting and tabs injection APIs no longer inject into moz-extension://documents. This change brings the API in line with broader efforts to discourage string-based code execution in extension contexts, alongside the default CSP that restricts script-src to extension URLs and the removal of remote source allowlisting in MV3 (bug 1581608).

Firefox emits a warning when this restriction is met, so you are aware of and can address any use of this process in your extensions. This is an example of the warning message:

Content Script execution in moz-extension document has been deprecated and it has been blocked

To work around this change,  you can:

  • Import scripts directly in the extension page’s HTML.
  • Use module imports or standard <script> tags in extension documents.
  • Restructure code to avoid dynamic code execution patterns. An extension can run code in its documents dynamically by registering a runtime.onMessage listener in the document’s script, then sending a message to trigger execution of the required code.

File access becomes opt-in

Target: Firefox 153

Extensions requesting file://*/ or <all_urls> currently trigger the “Access your data for all websites” permission message, and when granted, can run content scripts in file:-URLs. From Firefox 153, file access in extensions requires an opt-in for all extensions, including those already installed (bug 2034168).

pageAction SVG icon CSS filter (automatic color scheme)

Removed: Firefox 152

Firefox has been automatically applying a greyscale and brightness CSS filter to pageAction (address bar button) SVG icons when a dark theme is active. This was intended to improve contrast, but it actually reduced contrast for multi-color icons and caused poor visibility for some extensions, such as Firefox Multi-Account Containers.

For icons that adapt to light and dark color schemes, you can now use @media (prefers-color-scheme: dark) in the SVG icon, or the MV3 action manifest key, and specify theme_icons.

Here is an example of how to use a `prefers-color-scheme` media query in a pageAction SVG icon to control how the icon adapts to dark mode:

manifest.json

"page_action": {
  "default_icon": "icons/icon.svg"
}

icons/icon.svg

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
  <style>
    :root { color: black; }
    @media (prefers-color-scheme: dark) { :root { color: white; } }
  </style>
  <path fill="currentColor" d="M2 2h12v12H2z"/>
</svg>

Use of prefers-color-scheme media queries is also allowed in MV2 browserAction and MV3 action SVG icons as an alternative to the theme_icons manifest properties.

There are additional examples at the Mozilla Developer Network on how to test your extension pageAction icon with and without the implicit CSS filter.


New APIs & Capabilities

Now to the new stuff. Here, you get the ability to use popups without user activation, initial support for the new tab split view feature, and WebAuthn RP ID assertion.

openPopup without user activation (Firefox Desktop)

Available: Firefox 149 Desktop

action.openPopup() and browserAction.openPopup() no longer require a user gesture on Firefox Desktop. You can open your extension’s popup programmatically, e.g., in response to a native-messaging event, an alarm, or a background-script condition.

This change is part of the ongoing cross-browser alignment work in the WebExtensions Community Group to harmonize popup behavior across engines.

Example

Before (Firefox < 149): must hang off a user gesture, e.g., a context menu click:

browser.menus.create({
  id: "nudge",
  title: "Open popup",
  contexts: ["all"],
});

browser.menus.onClicked.addListener((info) => {
  if (info.menuItemId === "nudge") {
    browser.action.openPopup(); // user clicked the menu → allowed
  }
});

 

After (Firefox ≥ 149) — same intent, no user gesture needed, fires from a timer:

browser.alarms.create("nudge", { delayInMinutes: 1 });

browser.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === "nudge") {
    browser.action.openPopup(); // works without a click
  }
});

It’s the same call with the same result, but only the trigger changes from a user-action handler to any background event.

It’s the same call with the same result, but only the trigger changes from a user-action handler to any background event.

splitViewId in the tabs API

Available: Firefox 149

Firefox 149 introduces a new read-only splitViewId property on the tabs.Tab object to expose Firefox’s new split view feature (where two tabs are displayed side-by-side in one window). Split views are treated as one unit, and Web Extensions treat them the same way.

In Firefox 150, extensions can swap tabs within a split view. This update also resolves a confusing issue where using the user interface to reverse tab order incorrectly reports the tabs.onMoved event with inaccurate values. Additionally, Firefox introduces unsplitting behavior for web extensions: when tabs.move() is called with split-view tabs positioned separately (non-adjacently) in the array. Now, after the call, Firefox removes the split view rather than keeping the tabs locked together.

Here is an example of using the new splitViewId property.

// Log whenever a tab joins or leaves a split view.
browser.tabs.onUpdated.addListener((tabId, changeInfo) => {
  if (!("splitViewId" in changeInfo)) return;

  if (changeInfo.splitViewId === browser.tabs.SPLIT_VIEW_ID_NONE) {
    console.log(`Tab ${tabId} left its split view`);
  } else {
    console.log(`Tab ${tabId} joined split view ${changeInfo.splitViewId}`);
  }
});
// Firefox desktop also supports a filter to limite onUpdated events:
// }, { properties: ["splitViewId"] });

 

Firefox 151 enables extensions to move split views in tab groups. More improvements are coming, such as the ability to create split views from extensions (bug 2016928).

 

WebAuthn RP ID assertion

Available: Firefox 150

Previously, web extensions couldn’t use WebAuthn credentials registered on their company’s website or mobile apps. When extensions tried to set a custom Relying Party ID (RP ID) in navigator.credentials.create() or navigator.credentials.get(), Firefox rejected it with “SecurityError: The operation is insecure.”

With Firefox 150, Extensions can now assert a WebAuthn RP ID for any domain they have host permissions for

when calling navigator.credentials.create() or navigator.credentials.get(). This applies to both the publicKey.rp.id field during credential creation and the publicKey.rpId field during authentication.

A critical detail for server-side validation: When relying party servers validate credentials created by extensions, they must account for different origin formats across browsers. In Chrome, the origin follows the pattern chrome-extension://extensionid, which matches the extension’s location.origin. Firefox 150 introduces a new stable origin format: moz-extension://hash, where the hash is a 64-character SHA-256 representation of the extension ID (using characters a-p to represent hex values). Importantly, this hash-based origin is the same all users, unlike Firefox’s existing UUID-based moz-extension:// URLs used for extension documents.

To extract the origin from a credential for validation:

let clientData = JSON.parse(new TextDecoder().decode(
  publicKeyCredential.response.clientDataJSON
));
console.log(clientData.origin);

For more details, see Use Web Authn API in web extensions on MDN.

Summary

Change Type Firefox Version
executeScript / registerContentScript in moz-extension documents Deprecation → Removal Deprecated 149, removed 152
File access opt-in Change 153
pageAction SVG CSS filter Removal 152
openPopup() without user activation New capability 149 (Desktop only)
splitViewId on tabs.Tab New API 149
WebAuthn RP ID assertion New capability 150

Need more?

You can always find detailed information about WebExtensions API and Add-ons updates in the MDN release notes, e.g., for Firefox 149 and Firefox 150.

For any help or questions navigating any changes, don’t hesitate to post your topic on the Add-ons Discourse.

 

The post WebExtensions API Changes (Firefox 149-152) appeared first on Mozilla Add-ons Community Blog.

Thunderbird Blog

Mobile Progress Report – April 2026

It’s been a very busy couple of months as we’ve reworked processes & priorities and established a roadmap for both iOS and Android.  We are determining how best we can coordinate with the community, and think that our roadmap for the year has a good balance of fixes and features.  Today, I want to talk about our contributors and pull requests, Notifications in the Android app, progress in the iOS app, and an overview of our roadmap for both apps this year.

Contributors & Pull Requests

We are so grateful for the support and code contributions of many members, whether building items on our roadmap, improving the user experience, or, of course, translating.  As we work on our roadmap priorities, we will make time to review PRs and will discuss them weekly, and prioritize those that help solve issues and bugs or align with our roadmap items. Please be patient with our Pull Request pipeline.  Typically, in working with the community, we try to react very quickly.   

Roadmap

For Android, we’ve chosen the items on our roadmap because we think these will be the highest-impact features and bring the most value to everyone.  Our focus this year is to simplify and modernize the Android codebase.  This means reworking some of the architecture. This will be super helpful for us to move more quickly and will reduce complex bugs. The app has an older codebase, and like many older ones, it has its challenges. We have three full-time Android engineers and several community contributors, and we hope to better position ourselves to move quickly.  At a high level, Android is focusing on the rearchitecture, a better Message List experience, and Message Reader screens.  We are also simplifying how users can connect to Thunder Mail as we open it up.

Notifications

One thing that is at the top of my mind right now, too, is Push Notifications, specifically changes that Google has made to background processes, which affect our Notifications.  We are looking into what we can do to solve this, so know that it has become a top priority for us. I’ve been asked, “Why is it so hard for Thunderbird to get Push Notifications right?” and I wanted to speak to some of the challenges we have.  Most apps’ Notifications are triggered by their own web services, which then send Notifications through Apple or Google, who pass them to users.  But email is different. In an email client, we typically don’t own our own backend services, but other companies do (Microsoft, Google, Hotmail, Yahoo, Proton, etc.).  And they can have their own flavors of SMTP – how we get the emails, and no specific Push Notification implementation. 

So we have a work around: polling those providers ever X minutes asking for new emails, and triggering local notifications – but we can’t hook into a native Push Notification process like your banking app for example. This is under the IMAP implementation. The JMAP implementation (think modern email protocols) has something in place we can more readily consume.  Another challenge is how the battery is affected by how often we poll the providers, and we need specific permissions from Google to run this process in the background.  Those permissions changed recently which is why Notifications are having issues.

I’ve simplified some pieces here, but hopefully that gives you an idea of some of the complexity and tradeoffs that we are working with.  With all of that said, this is very important to us, and is our users’ biggest pain point.  It is becoming our biggest need for a fix.  I’ll give an update on where that sits within the roadmap next progress report when we have explored what solutions we can provide.

iOS Progress

For the iOS roadmap, everything is moving along well.  We have been wrapping up most of our IMAP & SMTP tickets, and we are moving into the Account Data pieces to manage accounts and authorizations.  We will also be having a new member join us in the next couple of weeks.  This will add some speed, but we’ve made good progress in getting the inner pieces together – what I consider the most complex parts.  As we move to more standard mobile backend pieces and more standard UI, we leave the world of unknown unknowns, and will be picking up steam.

At a high level our iOS roadmap is build out these screens:

  • Account Setup and Drawer
  • Messages: List, Reader, Compose, Search

And have these pieces in place: 

  • IMAP
  • SMTP
  • MIME
  • OAuth
  • Encryption
  • Email Composition 

And our target is still end of the year for the iOS release.  

Thank You!

Again we are so grateful to you, our community, for your support, and we are excited for this next quarter as we start to see the fruits of our labors.  

The post Mobile Progress Report – April 2026 appeared first on The Thunderbird Blog.

Wil Clouser

Firefox Sync adds official PostgreSQL support

The Sync Storage team has landed official PostgreSQL support for Firefox Sync.

Historically, Sync has only officially supported Google Spanner as a storage backend, with MySQL working unofficially. That has been a pretty high barrier to entry for people self-hosting their own services.

With PostgreSQL support, we hope to make self-hosting more approachable and continue supporting people who want the agency of hosting their data on infrastructure they control.

There is updated documentation for running it with Docker, including a one-shot docker compose setup:

https://mozilla-services.github.io/syncstorage-rs/how-to/how-to-run-with-docker.html

Mozilla is publishing Docker images for the PostgreSQL build here:

https://ghcr.io/mozilla-services/syncstorage-rs/syncstorage-rs-postgres

If you’ve been interested in self-hosting Sync but were put off by the storage requirements, take another look. If you run into bugs or have feedback, please file issues here:

https://github.com/mozilla-services/syncstorage-rs/issues

Jonathan Almeida

Gmail filters based on X-Phabricator-Stamps header

I want Phabricator emails to have a Gmail label so I can know which patches had me as a reviewer that then had follow-up comments from other folks.

This is useful for me when I review a patch and then I need to respond back to discussions in a more timely manner in comment threads that I've created.

It's difficult to do this today similar to Bugzilla Gmail filters because there are fewer identifiers that the more simplistic Gmail filter parameters can help with.

Today I learnt that there is an X-Phabricator-Stamps header in those Phabricator emails that let's you identify you as a the reviewer in a patch. So using that information, I wrote the Google script below to run every minute and avoid re-processing the same email twice.

A couple variables were added to the top and some console.logs are sprinkled around for my own debugging.

Code
var REVIEWER = "jonalmeida";
var LABEL_NAME = "Phabricator/Comments";
var BODY_MATCH = "commented on this revision.";
var SENDER = "phabricator@mozilla.com";
/**
 * Run once manually to install the per-minute trigger.
 */
function install() {
uninstall();
ScriptApp.newTrigger('processInbox')
  .timeBased()
  .everyMinutes(1)
  .create();
}
/**
 * Run once manually to remove the trigger.
 */
function uninstall() {
ScriptApp.getProjectTriggers().forEach(function(t) {
ScriptApp.deleteTrigger(t);
  });
PropertiesService.getScriptProperties().deleteProperty('lastRun');
}
/**
 * Every run, we try to avoid processing the same email twice because
 * there is no API trigger to run a script on every new email received.
 */
function processInbox() {
var props = PropertiesService.getScriptProperties();
var lastRun = parseInt(props.getProperty('lastRun') || '0');
var now = Math.floor(Date.now() / 1000);
// On first run, look back 2 minutes
if (lastRun === 0) {
lastRun = now - 120;
  }
var label = GmailApp.getUserLabelByName(LABEL_NAME);
if (!label) {
label = GmailApp.createLabel(LABEL_NAME);
  }
console.log("last run: " + lastRun);
var threads = GmailApp.search("from:" + SENDER + " after:" + lastRun);
console.log("threads to process: " + threads.length);
for (var i = 0; i < threads.length; i++) {
var thread = threads[i];
var messages = thread.getMessages();
console.log("messages to process: " + messages.length);
for (var j = 0; j < messages.length; j++) {
if (hasReviewerStamp(messages[j])) {
thread.addLabel(label);
console.log(thread.getFirstMessageSubject());
break;
      }
    }
  }
props.setProperty('lastRun', String(now));
}
function hasReviewerStamp(message) {
var raw = message.getRawContent();
var match = raw.match(/^X-Phabricator-Stamps:\s*(.+)$/m);
if (!match) {
return false;
  }
var stamps = match[1].trim().split(/\s+/);
return (stamps.indexOf("reviewer(@" + REVIEWER + ")") > -1) && raw.indexOf(BODY_MATCH) > -1;
}
/**
 * For debugging - see the list of labels you can search which
 * differs from what is used in the Gmail UI filter.
 */
function listAllLabels() {
console.log("All labels");
var labels = GmailApp.getUserLabels();
for (var i = 0; i < labels.length; i++) {
console.log(labels[i].getName());
  }
}

Mozilla Data YouTube Channel

Towards a Telemetry Taxonomy

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

Frederik Braun

Multiple things can be true at the same time

Dear reader. I am sure you have read a lot of blog posts about AI in the past weeks or months. And now I too am writing. Mostly to help me cope with what my kind of hacker people would call out as hypocrisy or cognitive dissonance.

There are various reasons to criticize AI because of its many, many externalities so here is a bit of a rant. If you keep reading, you will find a perspective on security at the end, but that is not the main goal of this blog post. The main goal is for me to cope. It is in no particularly order and I will allow myself to add paragraphs or links at the bottom. This post was last updated 2026-04-23 and will use "LLM" and "AI" interchangeably.

The summary of this blog post is likely "Multiple things can be true at the same time"

I am using LLMs and yet I am utterly unhappy with how they are built, trained, and run. AI is biased and reproducing harmful stereotypes about people that are non-confirming in one of many various ways.

I also see AI being used for decision-making and without questioning. It's unclear whether people are naive or intentionally using it to diffuse responsibility. Regardless, the people behind these systems imply that "their subjects" do not matter, when they allow AI to make decisions.

Even worse, all of modern society considers a proof of deep thought, care and analysis as long writing. All of history, all of human knowledge is in written form. AI is essentially seeding distrust in writing. I fear it may lead to a complete loss of what may have been previously considered objective and socially accepted Truths. What this means for education, and academia is still largely unclear. Not only is an industry killing it's career path for juniors, I fear we are killing the path for growth and thought of all coming generations.

AI is also bad for the environment, due to their excessive electrical power requirements. AI is making hardware expensive. Memory and storage are no longer affordable, most of it going straight to people who are buying it to fill a datacenter that has not yet been constructed and with money they don't have.

AI has plagiarized all the works that are (publicly) available on the internet. It destroys and devalues creative work. AI is also unpredictable. Given the stochastic nature of these systems, there is no way to make them reliable.

AI is also intensifying everything (Thank you BenVdS for the link). People are overwhelmed and exhausted - perceiving a pressure and restlessness because the AI must be fed a prompt. People succumb to feeling that an untyped prompt or an unread responses is wasted time. Even if the generated text isn’t even fully read. It must be replied to.

And yet, despite all of this, I am using LLMs.

While all of that appears true and important to me, I am also very enthusiastic about software security. My whole career is built on the analysis, composition and architecture of secure software. From time to time, working in security requires you to accept some harsh truths. As Felix "FX" Lindner used to say: You can’t argue with a root shell. Given an exploit - as a "proof by construction" - you can only admit there is a bug and face reality.

To be more specific, there was not just one single bug. We were given 14 bugs. Then we applied our in-house Firefox expertise, which led to us finding and fixing another 271 bugs,, many of which being sandbox escapes.

So, yes, as Graydon wrote, the capability increase of AI was "very sudden and very severe", that it caught us all by surprise. But if you are building secure software, protecting hundreds millions of users, you can’t take the moral high ground and sneer at those that touch the AI. As long as there are tools out there that give us a significant advantage over the current attackers, we need to adopt them.

Looking back, my head has been hurting for the past months, because of this tension - because of this paradox.

But in the end: Multiple things can be true at the same time.

Mozilla Performance Blog

Telemetry Alerting: How It Works

We recently released the telemetry alerting beta, and announced it in the blog post here! This blog post will dive into the details of how it works across Treeherder, and Mozdetect. At a high level, MozDetect handles the change point detection for telemetry probes, and Treeherder handles storing the detections, and producing the emails/bugs for these.

MozDetect

All of the existing, and any future change detection point techniques used for telemetry alerting are built in MozDetect. Having these live outside of Treeherder gives a low-barrier to entry for adding new features, and testing existing ones without having to set up everything needed for alerting in Treeherder. It’s built as a python module that is run through uv. This makes it very easy for anyone to run the code because of uv’s excellent python version, and dependency management. How to work with the code in this repository is outlined here, along with how to add your own techniques to it (note the access to mozdata through gcloud is required for this).

Detectors are split into two parts: (i) a detector that performs a comparison between two groups, and (ii) a detector that performs detection on a time series (using the detector from (i)). Our default detection technique, called  cdf_squared  lives here. The  timeseries_detector_name  is the name that will be used to access the detector from the telemetry probe side through the  change_detection_technique  field. The only method that absolutely needs to be implemented by these is the detect_changes method and it must return a list of Detection objects. These detection objects contain all the necessary information for producing an alert. There is also an  optional_detection_info  field that can contain additional things like attachments that would be added to Bugzilla bugs, and additional_data that can hold JSON data for storage in the DB. The cumulative distribution function (CDF) squared technique uses these to store the CDF before and after the detection along with a graph of these as an attachment for the Bugzilla bug.

Example of a CDF graph that is provided in bugs.

CDF Squared Detection Technique

The CDF squared technique detects changes in time-series histogram data by comparing CDFs between consecutive windows. It takes two CDFs, each representing the distribution of measurements over a time window, and computes the sum of squared differences between the two CDFs at each bin. The sign of the summed linear difference is then used to assign a direction to the squared difference score so that the output encodes whether the distribution moved to higher values (right shift) or lower values (left shift).

For time-series detection, this base comparison is applied in a rolling fashion across the full history of data. Each day’s 7-day smoothed CDF is compared against the next one, producing a continuous signal of squared CDF differences over time. A Butterworth low-pass filter is then applied to that signal to remove high-frequency noise while preserving genuine trend changes. Finally, scipy’s find_peaks function is used to locate statistically significant peaks and valleys in the filtered signal using a dynamic alert threshold based on the historical data. Information is extracted from those areas and then used to build the detection information needed for the alert generation process.

 

Alerting

Our alerting tooling lives in the Treeherder codebase. It’s run through our PerfSheriff Bot (called Sherlock) and runs once per day. When a detection is produced from MozDetect, a telemetry alert is added to the database and then the TelemetryAlertManager is called to handle it. The manager’s tasks are split into 6 ordered phases:

  1. Update alerts with changes from Bugzilla. This step ensures that any changes that happen in the bugs filed are mirrored into our database. Currently, we only track resolution changes here.
  2. Comment on existing bugs. This step is for updating existing bugs with information from new alerts. This step is not currently being used. In the future, this could be used to inform probe owners that a probe which doesn’t produce bugs has produced an alert in the same time range.
  3. File new bugs for alerts. This step handles filing bugs for any new alerts on probes set up for producing bugs.
  4. Modify existing bugs with new alerts. This step handles any modifications needed to existing bugs based on the new bugs that were created. Currently, the “See Also” field is modified for existing bugs to include the new bugs.
  5. Produce emails for new alerts. This step handles producing emails for any alerts set up to produce emails.
  6. Housekeeping. This step handles redoing any failures that happen above in either the current run or past runs. Currently, it’s being used to retry bug modifications and sending emails when we encounter a failure there. This excludes retrying bug filling since we delete the alert in that case and retry it the next time the alert is generated.

After the housekeeping step, the manager is done for the day and runs again on the next day to handle any updates and new alerts. Contrary to how alerting works for performance tests in CI, this process is fully automated and requires no human input at any point.

Setting up telemetry probes for alerting happens on the mozilla-central side in their probe schema using the new  monitor  field in the  metadata  section (example for email alerts, example for bug alerts). The telemetry alerting documentation has information about how to do this. We then use an index.json file from the telemetry dictionary to gather all the probes that should be alerting. The information there is supplemented by more granular information later in the pipeline to gather things like the time unit used for the probe to be able to better format the Bugzilla bug table.

Once a telemetry probe is set up for alerting and is found by our system, the owners (those listed in the email notification fields) will begin either receiving emails or have bugs produced for them. These can also be viewed by everyone on this dashboard.

Example of an alert being viewed in the dashboard.

 

 

Acknowledgements

Getting the project to this point involved work from people across multiple teams here at Mozilla. Special thanks to Eduardo Filho for his support on the telemetry probe side, to Bas Schouten for his guidance and work on the CDF Squared detection technique, and to Andrej Glavic and Beatrice Acasandrei for their help in reviewing the Treeherder-related changes.

If you hit any issues with the telemetry alerting system, or have any suggestions feel free to file a bug in the Testing :: Performance component or reach out to us in either #perf-help on Slack or in #perftest on Matrix.

Mozilla Data YouTube Channel

Data Incident Process

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