27 Mar 2026

feedPlanet Mozilla

Firefox Tooling Announcements: MozPhab 2.9.1 Released

Bugs resolved in Moz-Phab 2.9.1:

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

1 post - 1 participant

Read full topic

27 Mar 2026 7:41pm GMT

Cameron Kaiser: So long, cheesegrater

9To5Mac is reporting that Apple has confirmed the Mac Pro is no longer for sale, and indeed, although it was up yesterday, today it's gone.

And are you surprised? After all, Macs have their own bespoke GPUs now, and RAM is on-die. (Glad I sprang for the 16MB option on my M1 Air - that has greatly lengthened its useful service life.) If Apple isn't shipping computers with DIMM slots anymore, then why would they ship PCIe slots for anything else? It wasn't like there were many options you could put in the last iteration anyway, because it too had a non-upgradeable GPU and fixed RAM. Okay, okay, you could stick a whole bunch of NVMe sticks in it and it had good cooling. Was that worth it?

This marks the end of the venerable tower Macs that we loved in the PowerPC days. The Mac Studio is the new Mac Pro. We were always at war with Eastasia.

27 Mar 2026 3:22am GMT

26 Mar 2026

feedPlanet Mozilla

The Mozilla Blog: Mozilla and Mila announce strategic research partnership to advance open source and sovereign AI capabilities

Green and orange megaphone illustration on black background, symbolizing announcement or communication

The future of AI should belong to all of humanity, well beyond a handful of countries or companies. For that to happen, AI needs to be open, trusted, and built in ways that give people, institutions, and nations real choices. That's why, today, Mozilla is announcing a strategic partnership with Mila - Quebec Artificial Intelligence Institute to advance open source and sovereign AI capabilities.

This partnership marks a landmark strategic collaboration for both organizations and Mozilla's first-ever partnership with a major AI research lab. It is designed to grow over time, with an inaugural project that focuses on the intersection of trust and usability, including private memory architectures for AI agents.

Mila brings world-class research depth and a proven track record moving ideas into systems - from fundamental breakthroughs to applied tools and the diffusion of technology. Mozilla brings deep open source experience, a vibrant developer community, and the ecosystem instincts needed to turn research into something that spreads. The partnership is designed to show that open source AI can close the gap between cutting-edge research and real-world impact.

As we saw in the web era, having a robust open source software stack can democratize and accelerate innovation in dramatic ways. The same opportunity exists in AI - across compute, models, data, and developer experience - and much of the stack is already being built in the open. But gaps remain, particularly in the layers that determine whether AI is trustworthy, private, and built for a world with many languages, many cultures, and many legitimate ways of organizing society. If we can close those gaps, open source AI becomes a genuine option for the people and institutions that need it most.

"We are working to build a future where AI development is rooted in openness, privacy, and humanity," said Mark Surman, president of Mozilla. "This partnership is a delivery vehicle for that vision - and for breakthroughs that will help governments, developers, and companies alike. Canada can lead on AI sovereignty; we're joining with Mila to make it happen."

"Canada has what it takes to lead on frontier AI that the world can actually trust: the research depth, the values, and the will to do it differently. The next frontier in AI isn't just capability, it is trustworthiness, and Canada is uniquely positioned to lead on both. This partnership is a concrete step in that direction. Open, trustworthy AI isn't a compromise on ambition. It's the higher bar," said Valérie Pisano, president and CEO of Mila.

Together, Mila and Mozilla will develop the technologies and approaches that reduce dependence on closed systems and create more room for transparency, accountability, and shared innovation. The partnership also lays the groundwork for middle-power cooperation in AI: Open source projects have consistently provided the framework for technical collaboration across geographies and jurisdictions. Both organizations welcome research institutions, developers, and like-minded organizations to help fill the stack.

This is the first of what both organizations intend to be a sustained and growing body of work.

Read more about our Open Source AI Strategy here. Learn more about Mila here.

The post Mozilla and Mila announce strategic research partnership to advance open source and sovereign AI capabilities appeared first on The Mozilla Blog.

26 Mar 2026 4:58pm GMT

The Rust Programming Language Blog: Announcing Rust 1.94.1

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

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

rustup update stable

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

What's in 1.94.1

Rust 1.94.1 resolves three regressions that were introduced in the 1.94.0 release.

And a security fix:

Contributors to 1.94.1

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

26 Mar 2026 12:00am GMT

25 Mar 2026

feedPlanet Mozilla

Hacks.Mozilla.Org: Firefox Developer Edition and Beta: Try out Mozilla’s .rpm package!

In January, we introduced our Nightly package for RPM-based Linux distributions. Today, we are thrilled to announce it is now available for Firefox Beta!

Firefox Beta is great for testing your sites in a version of Firefox that will reach regular users in the coming weeks. If you find any issues, please file them on Bugzilla.

Switching to Mozilla's RPM repository allows Firefox Beta to be installed and updated like any other application, using your favorite package manager. It also provides a number of improvements:

If you have Mozilla's RPM repository already set up, you can simply install Firefox Beta with your package manager. Otherwise, follow the setup steps below.


If you are on Fedora (41+), or any other distribution using dnf5 as the package manager

sudo dnf config-manager addrepo --id=mozilla --set=baseurl=https://packages.mozilla.org/rpm/firefox --set=gpgkey=https://packages.mozilla.org/rpm/firefox/signing-key.gpg --set=gpgcheck=1 --set=repo_gpgcheck=0
sudo dnf makecache --refresh
sudo dnf install firefox-beta

Note: repo_gpgcheck=0 deactivate the signature of metadata with GPG. However, this is safeguarded instead by HTTPS and package signatures (gpgcheck=1).

If you are on openSUSE or any other distribution using zypper as the package manager

sudo rpm --import https://packages.mozilla.org/rpm/firefox/signing-key.gpg
sudo zypper ar --gpgcheck-allow-unsigned-repo https://packages.mozilla.org/rpm/firefox mozilla
sudo zypper refresh
sudo zypper install firefox-beta

For other RPM based distributions (RHEL, CentOS, Rocky Linux, older Fedora versions)

sudo tee /etc/yum.repos.d/mozilla.repo >  /dev/null << EOF
[mozilla]
name=Mozilla Packages
baseurl=https://packages.mozilla.org/rpm/firefox
enabled=1
gpgcheck=1
repo_gpgcheck=0
gpgkey=https://packages.mozilla.org/rpm/firefox/signing-key.gpg
EOF
# For dnf users
sudo dnf makecache --refresh
sudo dnf install firefox-beta
# For zypper users
sudo zypper refresh
sudo zypper install firefox-beta

The firefox-beta package will not conflict with your distribution's Firefox package if you have it installed, you can have both at the same time!

Adding language packs

If your distribution language is set to a supported language, language packs for it should automatically be installed. You can also install them manually with the following command (replace fr with the language code of your choice):

sudo dnf install firefox-beta-l10n-fr

You can list the available languages with the following command:

dnf search firefox-beta-l10n

Don't hesitate to report any problem you encounter to help us make your experience better.

The post Firefox Developer Edition and Beta: Try out Mozilla's .rpm package! appeared first on Mozilla Hacks - the Web developer blog.

25 Mar 2026 4:17pm GMT

This Week In Rust: This Week in Rust 644

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

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

Want TWIR in your inbox? Subscribe here.

Updates from Rust Community

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

Crate of the Week

This week's crate is noq, a general purpose implementation of the QUIC transport protocol in pure rust.

Thanks to Brendan O'Brien for the self-suggestion!

Please submit your suggestions and votes for next week!

Calls for Testing

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

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

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

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

Call for Participation; projects and speakers

CFP - Projects

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

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

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

CFP - Events

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

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

Updates from the Rust Project

433 pull requests were merged in the last week

Compiler
Library
Cargo
Clippy
Rust-Analyzer
Rust Compiler Performance Triage

Lot of mixed results this week. One big regression from #152931 makes the results look pretty negative, but otherwise the week was fairly quiet.

Triage done by @panstromek. Revision range: 5b61449e..6f22f613

Summary:

(instructions:u) mean range count
Regressions ❌
(primary)
1.0% [0.1%, 4.2%] 27
Regressions ❌
(secondary)
0.2% [0.0%, 0.6%] 36
Improvements ✅
(primary)
-0.1% [-0.2%, -0.1%] 3
Improvements ✅
(secondary)
-0.3% [-2.8%, -0.0%] 14
All ❌✅ (primary) 0.9% [-0.2%, 4.2%] 30

1 Regression, 1 Improvement, 4 Mixed; 1 of them in rollups 32 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

Cargo

Compiler Team (MCPs only)

Language Reference

No Items entered Final Comment Period this week for Rust RFCs, Language Team, Leadership Council or Unsafe Code Guidelines.

Let us know if you would like your PRs, Tracking Issues or RFCs to be tracked as a part of this list.

New and Updated RFCs

Upcoming Events

Rusty Events between 2026-03-25 - 2026-04-22 🦀

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

Code does not become better out of thin air just because you rewrite it in #rustlang.

- allp on mastodon

Despite a third week gone by without a suggestion, llogiq is unrelenting in his quest to find a quote worth your while.

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

25 Mar 2026 4:00am GMT

Jonathan Almeida: Use Android Studio for resolving conflicts in Jujutsu

You can use JJ's built-in editor for conflict resolutions, but I've found it difficult to follow. A recommendation from co-workers was to use Meld and that has worked quite well once I (begrudingly) accepted that I needed to download another single-purpose app.

Today, another co-worker Andrey Zinovyev found out that we can use Android Studio's (IntelliJ IDEA's really) built-in merge tool to resolve the three-way merge. This is more convenient for me since I spend most of my time here already, so using it as a general purpose merge editor for my work projects is quite nice.

[ui]
merge-editor = "studio"

[merge-tools.studio]
merge-args = ["merge", "$left", "$right", "$base", "$output"]
program = "/Users/jalmeida/Applications/Android Studio Nightly.app/Contents/MacOS/studio"

Presto!

25 Mar 2026 12:00am GMT

24 Mar 2026

feedPlanet Mozilla

The Mozilla Blog: A free VPN you can trust, now built into Firefox

A Firefox browser window displays a fox illustration with a visible VPN status indicator showing “VPN is on” and remaining data for the month.

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.

A Firefox browser window displays a fox illustration with a visible VPN status indicator showing “VPN is on” and remaining data for the month.

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.

The Firefox logo

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.

24 Mar 2026 4:00pm GMT

Firefox Developer Experience: Firefox WebDriver Newsletter 149

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 149 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 149, multiple WebDriver bugs were fixed by contributors:

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

24 Mar 2026 1:23pm GMT

23 Mar 2026

feedPlanet Mozilla

The Mozilla Blog: Try Tab Notes in Firefox to leave a note on any page

Firefox tab notes feature showing “Add note” popup to save information about an open tab

Don't remember why you have all those webpages open? Now you can leave yourself a note for any tab.

Tab Notes - our latest experimental feature in Firefox - are designed to help you remember, reflect, and pick up where you left off on the web by letting you attach a short note to a webpage.

Indicated by a sticky note icon and visible when hovering over tabs, Tab Notes notes remain connected to the page's URL until you delete them. Your notes are yours. They remain private and accessible only to you. Firefox stores them locally in your browser and doesn't send them to Mozilla.

Firefox hover preview showing an Apple Developer Menus page open in a background tab.

Starting March 24, you can try Tab Notes by following these steps:

Firefox Labs settings page in dark mode showing Tab notes enabled as an experimental productivity feature.

Now you're all set! Just right-click or hover over a tab and choose "Add Note" to create your first tab note!

Firefox tab note popup on a Linzer Cookies recipe page with the typed note “print recipe for mom.”

This work is inspired by user research that we conducted last year, which explored how people resume tasks after interruptions. One key insight we learned is that when we are interrupted, even a small reminder or message can significantly improve our ability to resume a task.

Many people use a variety of analog (e.g., sticky notes) and digital tools (e.g., note-taking apps) for these purposes as well, and Tab Notes are our exploration of that idea in a practical, lightweight way. These notes are easy to create, edit, and delete.

This is an early experiment, part of the Firefox Labs program. We are eager for feedback, which you can share on Mozilla Connect or by filing a ticket in Bugzilla.

The Firefox logo

Take control of your internet

Download Firefox

The post Try Tab Notes in Firefox to leave a note on any page appeared first on The Mozilla Blog.

23 Mar 2026 7:00pm GMT

The Mozilla Blog: Split View in Firefox: Two tabs side by side, right where you need them

Firefox split view showing London travel page and tab selection menu for adding website, video, or article.

Much of what we do on the web involves looking at more than one thing at a time - booking tickets while checking your calendar, taking notes as you go through a report, or comparing options before making a purchase.

The web is inherently multidimensional. For years, browsing this way meant bouncing back and forth between multiple open tabs, or spinning up multiple windows and using other tools to organize them side-by-side.

The new Split View feature makes these moments easier. It lets you place two tabs next to each other in the same Firefox window so you can see both at once and keep the context you need right in front of you.

Split View is available to all Firefox users starting with Firefox 149, rolling out on March 24. If you'd like to give it a go:

Firefox split view feature showing two tabs side by side with tab selection menu in dark mode

How the Firefox team uses Split View

The team behind Split View has been using it actively over the past few months, and a few workflows quickly stood out. Here are some of the ways people on our team have been using it:

Planning and comparing

Sometimes, you just need two things visible at once.

Gabriel: I've been using Split View to plan camping trips. I open a map on one side and a campsite booking page on the other. This makes it easy to explore locations and check availability without constantly switching tabs.

Firefox split screen comparing campsite reservations with map for planning a trip.

Everyday tasks

Split View is also helpful for small administrative tasks, the kind that involve copying information from one place to another.

Jonathan: I used Split View while filing my taxes. All my documents - W-2s and other forms - were online, so I kept them open on one side while filling things out on the FreeTaxUSA site on the other. Having both visible made the process much easier.

Note-taking

Ania: I often use Split View when reading and writing at the same time. I'll keep a PDF or article open on one side and take notes on the other as I go. Recently, I've been using this setup while preparing notes for my reading group. It helps me stay focused and quickly organize what I want to share.

Firefox split screen with PDF on left and Gmail notes draft on right for research and writing.

What's next for Split View

We built Split View to support the way people naturally move through information on the web - comparing, referencing and writing along the way. This first version focuses on making the most common side-by-side workflows easy.

If you try it, we'd love your feedback on how it fits into your day-to-day browsing and what would make it even more useful.

The Firefox logo

Take control of your internet

Download Firefox

The post Split View in Firefox: Two tabs side by side, right where you need them appeared first on The Mozilla Blog.

23 Mar 2026 7:00pm GMT

Mozilla Privacy Blog: Competition, Innovation, and the Future of the Web – Why Independent Browser Engines Matter

Gecko matters because it ensures there's an independent voice shaping how the internet evolves. Without Gecko, the landscape would be dominated by Apple and Google alone.

From accessing information, communicating with others, shopping, working, learning, and entertainment, the vast majority of our time online is spent within a browser. While there are many browsers out there, there are only a few browser engines, the technology necessary to render the data that makes up the web as websites we can use.

Browser engines are among the most complex and consequential pieces of infrastructure on the modern internet. They determine how web standards are implemented, how security and privacy protections are enforced, and which actors ultimately shape the evolution of the web.

As the internet increasingly fragments into walled gardens, and as new technologies like artificial intelligence (AI) are integrated directly into browsers, the influence of browser engines is only growing. When innovation is built on a single dominant engine, it concentrates technical and economic power, narrows choice, and risks steering the web toward the priorities of a few large platforms rather than the public interest.

Gecko is Mozilla's browser engine that powers Firefox. It is one of only three widely used engines and the only independent browser engine. In other words, it is not governed by a company that also runs an operating system to distribute their own browser.

Why Browser Engines Matter

Browser engines (not to be confused with search engines) are the lesser-known technology powering your web browsers.

As the core software layer responsible for interpreting and rendering web content, browser engines play the fundamental role of turning HTML, CSS, and JavaScript into webpages users can interact with.

While browsers are user-facing products, engines are the layer where structural decisions about the web are made. Examples include privacy and security protections, performance characteristics, and the support of APIs. Browser engines are at the heart of the web.

Gecko and the Browser Monoculture

The browser engine landscape is highly concentrated. In 2013, there were five major browser engines. In 2026, there are only three left: Apple's WebKit (which companies are required to use to build on iOS), Google's Blink, and Mozilla's Gecko. Gecko is the only remaining independent browser engine and it powers Firefox.

When engine diversity declines, so does the practical ability to challenge dominant business models or introduce alternative implementations that can put users first through security, privacy, or other features.

There are only three major browser engines left - Apple's WebKit, Google's Blink and Gecko from Mozilla. Apple's WebKit mainly runs on Apple devices, making Gecko the only cross-platform challenger to Blink.

This concentration increasingly risks hard-coding a single company's technical assumptions into the future of the web. Market pressures often turn standards-compliant but differing implementation choices into "bugs" that need fixing.

As both human and AI-driven browsing expand in use, choices about API implementation, data access, and security boundaries at the browser engine level become even more critical. A monoculture at the engine layer could extend to producing a monoculture in AI browsing experiences as well.

Maintaining an Independent Browser Engine Allows Mozilla to be More User-centric

Gecko, as an independent browser engine, tangibly allows Mozilla to build and operate in a way that is aligned with our mission: keeping the web open, secure, privacy-first, and accessible to everyone. It ensures that Mozilla is not only advocating for these principles but actively building the underlying infrastructure that makes them possible.

Through Gecko, we have the freedom to design and ship features based on what is best for users, rather than what is easiest or most profitable within another company's technology stack.

In practice, this enables us to:

If a small number of vertically integrated companies (AI assistants, search, operating systems, ads) completely control browser engines, then competition, transparency, and user choice on the open web will be much harder to achieve. They will have strong incentives to favour their own services, limit interoperability, and steer defaults and standards to their advantage.

Maintaining an independent engine also lowers barriers for others. Newer entrants to the browser space can rely on interoperability as defined in specifications. If they are not building their own engine, building on Gecko can help sustain a more competitive browser ecosystem. Engine diversity at this foundational layer enables innovation, which is shaped by multiple actors and multiple visions, rather than it being dictated by a single dominant platform.

Browser Engine Plurality Ensures Tech is Built For People, Not Shareholders

In an era defined by platform consolidation and AI-driven change, browser engines can't be treated as invisible infrastructure. Independent engines like Gecko provide a structural counterbalance. Browser engine plurality is needed to ensure competition, transparency, and technology built for people, not shareholders.

As governments increasingly focus on security, resilience and sustainable growth, browser engine competition has a central role to play in avoiding single points of vulnerability or failure. Meaningful competition and a focus on open source approaches help ensure that economies are not locked into a single company's infrastructure and that governments, companies, and people retain real choice over where to build and how to optimize for their needs.

Mozilla has long engaged with policymakers and regulators on the importance of competition and openness at the browser and engine layer. As the web and broader technology landscape continue to evolve, especially in the face of AI, we will continue to advance policies that protect engine diversity, promote fair competition, and ensure the web evolves in the public interest.

The post Competition, Innovation, and the Future of the Web - Why Independent Browser Engines Matter appeared first on Open Policy & Advocacy.

23 Mar 2026 3:13pm GMT

22 Mar 2026

feedPlanet Mozilla

Niko Matsakis: Maximally minimal view types, a follow-up

A short post to catalog two interesting suggestions that came in from my previous post, and some other related musings.

Syntax with .

It was suggested to me via email that we could use . to eliminate the syntax ambiguity:

let place = &mut self.{statistics};

Conceivably we could do this for the type, like:

fn method(
    mp: &mut MessageProcessor.{statistics},
    ...
)

and in self position:

fn foo(&mut self.{statistics}) {}

I have to sit with it but…I kinda like it?

I'll use it in the next example to try it on for size.

Coercion for calling public methods that name private types

In my post I said that if you hvae a public method whose self type references private fields, you would not be able to call it from another scope:

mod module {
    #[derive(Default)]
    pub struct MessageProcessor {
        messages: Vec<String>,
        statistics: Statistics,
    }
    
    pub struct Statistics { .. }

    impl MessageProcessor {
        pub fn push_message(
            &mut self.{messages},
            //         -------- private field
            message: String,
        ) {}
    }
}

pub fn main() {
    let mp = MessageProcessor::default();
    mp.push_message(format!("Hi"));
    // ------------ Error!
}

The error arises from desugaring push_message to a call that references private fields:

MessageProcessor::push_message(
    &mut mp.{messages},
    //       -------- not nameable here
    format!("Hi"),
)

I proposed we could lint to avoid this situation.

But an alternative was proposed where we would say that, when we introduce an auto-ref, if the callee references local variables not visible from this point in the program, we just borrow the entire struct rather than borrowing specific fields.

So then we would desugar to:

MessageProcessor::push_message(
    &mut mp,
    //   -- borrow the whole struct
    format!("Hi"),
)

If we then say that &mut MessageProcessor is coercable to a &mut MessageProcessor.{messages}, then the call would be legal.

Interestingly, the autoderef loop already considers visibility: if you do a.foo, we will deref until we see a foo field visible to you at the current point.

Oh and a side note, assigning etc

This raises an interesting question I did not discuss. What happens when you write a value of a type like MessageProcessor.{messages}?

For example, what if I do this:

fn swap_fields(
    mp1: &mut MessageProcessor.{messages},
    mp2: &mut MessageProcessor.{messages},
) {
    std::mem::swap(mp1, mp2);
}

What I expect is that this would just swap the selected fields (messages, in this case) and leave the other fields untouched.

The basic idea is that a type MessageProcessor.{messages} indicates that the messages field is initialized and accessible and the other fields must be completely ignored.

Another possible future extension: moved values

This represents another possible future extension. Today if you move out of a field in a struct, then you can no longer work with the value as a whole:

impl MessageProcessor {
    fn example(mut self) {
        // move from self.statistics
        std::mem::drop(self.statistics);
        
        // now I cannot call this method,
        // because I can't borrow `self`:
        self.push_message(format!("Hi again"));
    }
}

But with selective borrowing, we could allow this, and you could even return "partially initialized" values:

impl MessageProcessor {
    fn take_statistics(
        mut self,
    ) -> MessageProcessor.{messages} {
        std::mem::drop(self.statistics);
        self
    }
}

That'd be neat.

22 Mar 2026 4:52pm GMT

Jonathan Almeida: Use |mach try --no-push| for a configuration dry run

I wanted to see what the generated try configuration would be for a new preset I made and did this by submitting real try pushes (with empty so they don't execute resources). What I was looking for was "dry run" in the help files, but I recently discovered it to be --no-push.

$ jj try-push --preset fenix --no-push # 'fenix' as an example preset
Artifact builds enabled, pass --no-artifact to disable
Commit message:
Fuzzy (preset: fenix) query='build-apk-fenix-debug&query='signing-apk-fenix-debug&query='build-apk-fenix-android-test-debug&query='signing-apk-fenix-android-test-debug&query='test-apk-fenix-debug&query='ui-test-apk-fenix-arm-debug&query=^source-test 'fenix&query='generate-baseline-profile-firebase-fenix

mach try command: `./mach try --preset fenix --no-push`

Pushed via `mach try fuzzy`
Calculated try_task_config.json:
{
    "parameters": {
        "optimize_target_tasks": false,
        "try_task_config": {
            "disable-pgo": true,
            "env": {
                "TRY_SELECTOR": "fuzzy"
            },
            "tasks": [
                "build-apk-fenix-android-test-debug",
                "build-apk-fenix-debug",
                "generate-baseline-profile-firebase-fenix",
                "source-test-android-detekt-detekt-fenix",
                "source-test-android-l10n-lint-l10n-lint-fenix",
                "source-test-android-lint-fenix",
                "source-test-buildconfig-buildconfig-fenix",
                "source-test-ktlint-fenix",
                "source-test-mozlint-android-fenix",
                "test-apk-fenix-debug",
                "ui-test-apk-fenix-arm-debug",
                "ui-test-apk-fenix-arm-debug-smoke"
            ],
            "use-artifact-builds": true
        }
    },
    "version": 2
}

Here, jj try-push is my quick alias around ./mach try for personal simplicity with my workflow.

22 Mar 2026 12:00am GMT

Jonathan Almeida: Create new revisions in Jujutsu with multiple heads

It was one of those "ah ha!" moments for me when I finally used it. Chris Krycho covers the concept of megamerges with this diagram:

       m --- n
      /       \
a -- b -- c -- [merge] -- [wip]
      \       /
       w --- x

I've found a more realistic example that best relates to my natural workflow: implementing feature (A) benefitted from having the changes of another tooling patch upgrade (B), that lead to discovering and fixing a bug (C).

      (B)
       m ----- n
      /         \           (A)
a -- b --------- [merge] --- y -- z
      \                     /      \                (C)
        -------------------         ----- [merge] -- w -- x
        \                                /
          -------------------------------

In this case, trying to separate these into distinct streams of work is quite logically, but we also don't need to leave them unlinked so that they can benefit from each other.

This is what my jj log ended up looking like:

@  oppmsuvz jxxxxxxxxxxxx@gmail.com 2026-03-22 00:34:10 firefox@ 05259417
Bug xxxxxxx - Simplify the tests
ultowtnr jxxxxxxxxxxxx@gmail.com 2026-03-22 00:34:04 100c4cce
Bug xxxxxxx - Include private flag in ShareData
lorusmuo jxxxxxxxxxxxx@gmail.com 2026-03-21 20:19:30 905b0460
├─╮  (empty) (no description set)
│ ○  sumqskuu jxxxxxxxxxxxx@gmail.com 2026-03-21 04:22:00 92f6028b
│ │  Add a new secret settings fragment
│ ○  oylmprpu jxxxxxxxxxxxx@gmail.com 2026-03-21 04:22:00 18931825
│ │  Create a new feature for receiving and sending commands.
│ ○  xrnnoonu jxxxxxxxxxxxx@gmail.com 2026-03-21 04:21:48 618020c7
╭─┤  (empty) (no description set)
│ ○  rqlyqqzx jxxxxxxxxxxxx@gmail.com 2026-03-19 17:20:20 c9b5323c
│ │  Bug xxxxxxx - Part 2: Create new android gradle module skill
│ ○  txvozpwz jxxxxxxxxxxxx@gmail.com 2026-03-19 17:20:13 cee18510
├─╯  Bug xxxxxxx - Part 1: Add new gradle example module
◆  pwsnmryn vxxxxxxxxxxxx@gmail.com 2026-03-18 13:21:47 main@origin fa20ce29
Bug xxxxxxx - Make my feature work for everyone
~

When I need to submit these, [moz-phab][1 has support for specifying revset ranges with moz-phab start_rev end_rev. However, I can also use jj rebase -s <rev> -d main@origin to put out some try pushes to validate they still work separately - so far, no conflicts in this step.

22 Mar 2026 12:00am GMT

21 Mar 2026

feedPlanet Mozilla

Niko Matsakis: Maximally minimal view types

This blog post describes a maximally minimal proposal for view types. It comes out of a converastion at RustNation I had with lcnr and Jack Huey, where we talking about various improvements to the language that are "in the ether", that basically everybody wants to do, and what it would take to get them over the line.

Example: MessageProcessor

Let's start with a simple example. Suppose we have a struct MessageProcessor which gets created with a set of messages. It will process them and, along the way, gather up some simple statistics:

pub struct MessageProcessor {
    messages: Vec<String>,
    statistics: Statistics,
}

#[non_exhaustive] // Not relevant to the example, just good practice!
pub struct Statistics {
    pub message_count: usize,
    pub total_bytes: usize,
}

The basic workflow for a message processor is that you

Accumulating messages

Accumulating messages is easy:

impl MessageProcessor {
    pub fn push_message(&mut self, message: String) {
        self.messages.push(message);
    }
}

Processing a single message

The function to process a single message takes ownership of the message string because it will send it to another thread. Before doing so, it updates the statistics:

impl MessageProcessor {
    fn process_message(&mut self, message: String) {
        self.statistics.message_count += 1;
        self.statistics.total_bytes += message.len();
        // ... plus something to send the message somewhere
    }
}

Draining the accumulated messages

The final function you need is one that will drain the accumulated messages and process them. Writing this ought to be straightforward, but it isn't:

impl MessageProcessor {
    pub fn process_pushed_messages(&mut self) {
        for message in self.messages.drain(..) {
            self.process_message(message); // <-- ERROR: `self` is borrowed
        }
    }
}

The problem is that self.messages.drain(..) takes a mutable borrow on self.messages. When you call self.process_message, the compiler assumes you might modify any field, including self.messages. It therefore reports an error. This is logical, but frustrating.

Experienced Rust programmers know a number of workarounds. For example, you could swap the messages field for an empty vector. Or you could invoke self.messages.pop(). Or you could rewrite process_message to be a method on the Statistics type. But all of them are, let's be honest, suboptimal. The code above is really quite reasonable, it would be nice if you could make it work in a straightforward way, without needing to restructure it.

What's needed: a way for the borrow checker to know what fields a method may access

The core problem is that the borrow checker does not know that process_message will only access the statistics field. In this post, I'm going to focus on an explicit, and rather limited, notation, but I'll also talk about how we might extend it in the future.

View types extend struct types with a list of fields

The basic idea of a view type is to extend the grammar of a struct type to optionally include a list of accessible fields:

RustType := StructName<...>
         |  StructName<...> { .. }         // <-- what we are adding
         |  StructName<...> { (fields),* } // <-- what we are adding

A type like MessageProcessor { statistics } would mean "a MessageProcessor struct where only the statistics field can be accessed". You could also include a .., like MessageProcessor { .. }, which would mean that all fields can be accessed, which is equivalent to today's struct type MessageProcessor.

View types respect privacy

View types would respect privacy, which means you could only write MessageProcessor { messages } in a context where you can name the field messages in the first place.

View types can be named on self arguments and elsewhere

You could use this to define that process_message only needs to access the field statistics:

impl MessageProcessor {
    fn process_message(&mut self {statistics}, message: String) {
        //             ----------------------
        //             Shorthand for: `self: &mut MessageProcessor {statistics}`
        
        // ... as before ...
    }
}

Of course you could use this notation in other arguments as well:

fn silly_example(.., mp: &mut MessageProcessor {statistics}, ..) {
}

Explicit view-limited borrows

We would also extend borrow expressions so that it is possible to specify precisely which fields will be accessible from the borrow:

let messages = &mut some_variable {messages}; // Ambiguous grammar? See below.

When you do this, the borrow checker produces a value of type &mut MessageProcessor {messages}.

Sharp-eyed readers will note that this is ambiguous. The above could be parsed today as a borrow of a struct expression like some_variable { messages } or, more verbosely, some_variable { messages: messages }. I'm not sure what to do about that. I'll note some alternative syntaxes below, but I'll also note that it would be possible for the compiler to parse the AST in an ambiguous fashion and disambiguate later on once name resolution results are known.

We automatically introduce view borrows in an auto-ref

In our example, though, the user never writes the &mut borrow explicitly. It results from the auto-ref added by the compiler as part of the method call:

pub fn process_pushed_messages(&mut self) {
    for message in self.messages.drain(..) {
        self.process_message(message); // <-- auto-ref occurs here
    }
}

The compiler internally rewrites method calls like self.process_message(message) to fully qualified form based on the signature declared in process_message. Today that results in code like this:

MessageProcessor::process_message(&mut *self, message)

But because process_message would now declare &mut self { statistics }, we can instead desugar to a borrow that specifies a field set:

MessageProcessor::process_message(&mut *self { statistics }, message)

The borrow checker would respect views

Integrating views into the borrow checker is fairly trivial. The way the borrow checker works is that, when it sees a borrow expression, it records a "loan" internally that tracks the place that was borrowed, the way it was borrowed (mut, shared), and the lifetime for which it was borrowed. All we have to do is to record, for each borrow using a view, multiple loans instead of a single loan.

For example, if we have &mut self, we would record one mut-loan of self. But if we have &mut self {field1, field2}, we would two mut-loans, one of self.field1 and one of self.field2.

Example: putting it all together

OK, let's put it all together. This was our original example, collected:

pub struct MessageProcessor {
    messages: Vec<String>,
    statistics: Statistics,
}

#[non_exhaustive]
pub struct Statistics {
    pub message_count: usize,
    pub total_bytes: usize,
}

impl MessageProcessor {
    pub fn push_message(&mut self, message: String) {
        self.messages.push(message);
    }

    pub fn process_pushed_messages(&mut self) {
        for message in self.messages.drain(..) {
            self.process_message(message); // <-- ERROR: `self` is borrowed
        }
    }

    fn process_message(&mut self, message: String) {
        self.statistics.message_count += 1;
        self.statistics.total_bytes += message.len();
        // ... plus something to send the message somewhere
    }
}

Today, process_pushed_messages results in an error:

pub fn process_pushed_messages(&mut self) {
    for message in self.messages.drain(..) {
        //         ------------- borrows `self.messages`
        self.process_message(message); // <-- ERROR!
        //   --------------- borrows `self`
    }
}

The error arises from a conflict between two borrows:

But in the "brave new world", we'll modify the program in one place:

-    fn process_message(&mut self, message: String) {
+    fn process_message(&mut self {statistics}, message: String) {

and as a result, the process_pushed_messages function will now borrow check successfully. This is because the two loans are now issued for different places:

At runtime, this is still just a pointer

One thing I want to emphasize is that "view types" are a purely static construct and do not change how things are compiled. They simply give the borrow checker more information about what data will be accessed through which references. The process_message method, for example, still takes a single pointer to self.

This is in contrast with the workarounds that exist today. For example, if I were writing the above code, I might well rewrite process_message into an associated fn that takes a &mut Statistics:

impl MessageProcessor {
    fn process_message(statistics: &mut Statistics, message: String) {
        statistics.message_count += 1;
        statistics.total_bytes += message.len();
        // ... plus something to send the message somewhere
    }
}

This would be annoying, of course, since I'd have to write Self::process_message(&mut self.statistics, ..) instead of self.process_message(), but it would avoid the borrow check error.

Beyond being annoying, it would change the way the code is compiled. Instead of taking a reference to the MessageProcessor it now takes a reference to the Statistics.

In this example, the change from one type to another is harmless, but there are other examples where you need access to mulitple fields, in which case it is less efficient to pass them individually.

Frequently asked questions

How hard would this be to implement?

Honestly, not very hard. I think we could ship it this year if we found a good contributor who wanted to take it on.

What about privacy?

I would require that the fields that appear in view types are 'visible' to the code that is naming them (this includes in view types that are inserted via auto-ref). So the following would be an error:

mod m {
    #[derive(Default)]
    pub struct MessageProcessor {
        messages: Vec<String>,
        ...
    }
    
    impl MessageProcessor {
        pub fn process_message(&mut self {messages}, message: String) {
            //                           ----------
            //   It's *legal* to reference a private field here, but it
            //   results in a lint, just as it is currently *legal*
            //   (but linted) for a public method to take an argument of
            //   private type. The lint is because doing this is effectively
            //   going to make the method uncallable from outside this module.
            self.messages.push(message);
        }
    }
}

fn main() {
    let mut mp = m::MessageProcessor::default();    
    mp.process_message(format!("Hello, world!"));
    // --------------- ERROR: field `messages` is not accessible here
    //
    // This desugars to:
    // 
    // ```
    // MessageProcessor::process_message(
    //     &mut mp {messages},        // <-- names a private field!
    //     format!("Hello, world!"),
    // )
    // ```
    // 
    // which names the private field `messages`. That is an error.
}

Does this mean that view types can't be used in public methods?

More-or-less. You can use them if the view types reference public fields:

#[non_exhaustive]
pub Statistics {
    pub message_count: usize,
    pub average_bytes: usize,
    // ... maybe more fields will be added later ...
}

impl Statistics {
    pub fn total_bytes(&self {message_count, average_bytes}) -> usize {
        //                    ----------------------------
        //             Declare that we only read these two fields.
        self.message_count * self.average_bytes
    }
}

Won't it be limited that view types more-or-less only work for private methods?

Yes! But it's a good starting point. And my experience is that this problem occurs most often with private helper methods like the one I showed here. It can occur in public contexts, but much more rarely, and in those circumstances it's often more acceptable to refactor the types to better expose the groupings to the user. This doesn't mean I don't want to fix the public case too, it just means it's a good use-case to cut from the MVP. In the future I would address public fields via abstract fields, as I described in the past.

What if I am borrowing the same sets of fields over and over? That sounds repititive!

That's true! It will be! I think in the future I'd like to see some kind of 'ghost' or 'abstract' fields, like I described in my abstract fields blog post. But again, that seems like a "post-MVP" sort of problem to me.

Must we specify the field sets being borrowed explicitly? Can't they be inferred?

In the syntax I described, you have to write &mut place {field1, field2} explicitly. But there are many approaches in the literature to inferring this sort of thing, with row polymorphism perhaps being the most directly applicable. I think we could absolutely introduce this sort of inference, and in fact I'd probably make it the default, so that &mut place always introduces a view type, but it is typically inferred to "all fields" in practice. But that is a non-trivial extension to Rust's inference system, introducing a new kind of inference we don't do today. For the MVP, I think I would just lean on auto-ref covering by far the most common case, and have explicit syntax for the rest.

Man, I have to write the fields that my method uses in the signature? That sucks! It should be automatic!

I get that for many applications, particularly with private methods, writing out the list of fields that will be accessed seems a bit silly: the compiler ought to be able to figure it out.

On the flip side, this is the kind of inter-procedural inference we try to avoid in Rust, for a number of reasons:

The bottom line for me is one of staging: whatever we do, I think we will want a way to be explicit about exactly what fields are being accessed and where. Therefore, we should add that first. We can add the inference later on.

Why does this need to be added to the borrow checker? Why not desugar?

Another common alternative (and one I considered for a while…) is to add some kind of "desugaring" that passes references to fields instead of a single reference. I don't like this for two reasons. One, I think it's frankly more complex! This is a fairly straightforward change to the borrow checker, but that desugaring would leave code all over the compiler, and it would make diagnostics etc much more complex.

But second, it would require changes to what happens at runtime, and I don't see why that is needed in this example. Passing a single reference feels right to me.

What about the ambiguous grammar? What other syntax options are there?

Oh, right, the ambiguous grammar. To be honest I've not thought too deeply about the syntax. I was trying to have the type Struct { field1, field 2 } reflect struct constructor syntax, since we generally try to make types reflect expressions, but of course that leads to the ambiguity in borrow expressions that causes the problem:

let foo = &mut some_variable { field1 };
            // ------------- is this a variable or a field name?

Options I see:

Conclusion: this is a good MVP, let's ship it!

In short, I don't really see anything blocking us from moving forward here, at least with a lang experiment.

21 Mar 2026 4:37pm GMT