The Complete Computer

Mozilla Firefox: The User Agent of Choice on Arroyo Systems

LifeTechEmacsArcology

In search of A Modern User Agent . As of [2025-03-17 Mon] the user agent looks like this:

Declarative Firefox Installation with Nix

This noweb document constructs a customized Firefox installation:

  • I deploy firefox with settings in both about:config below and enterprise policies further down .

  • userChrome.css and userContent.css loaded from A Custom Firefox User Chrome below

  • Some desktop integration settings are set for KDE via XDG and indicating that we should use Wayland APIs and XDG Portal for file dialogs/print dialogs etc...

nix source: :tangle ~/arroyo-nix/hm/firefox.nix :noweb yes -r
{ config, pkgs, ... }: { xdg.mimeApps.enable = true; xdg.mimeApps.defaultApplications = { "x-scheme-handler/http" = ["firefox.desktop"]; "x-scheme-handler/https" = ["firefox.desktop"]; }; home.sessionVariables = { GTK_USE_PORTAL = 1; # (ref:gtk-use-portal) MOZ_ENABLE_WAYLAND = 1; }; programs.firefox = { enable = true; package = <<myFirefox>> ; profiles.default = { name = "default"; isDefault = true; settings = { <<profileConfig>> }; userChrome = builtins.readFile ../files/userChrome.css; userContent = builtins.readFile ../files/userContent.css; }; }; }

Firefox Customizations

I customize my Firefox in the funroll loops fashion; the browser I ship in my Arroyo Systems is highly customized, and reliant on some mozilla-hosted cloud services. I am mostly ambivalent about this -- I believe I can still exit to a self-hosted sync server and a fork like LibreWolf if they continue to backslide as a Looming antitrust inferno against Google could burn Mozilla, too. It's not quite worth the trouble yet compared to opting out of features and enabling just enough telemetry to let them know my discontent. (See also: Zen and the art of Cringe below)

Through a combination of browser extensions, about:config changes, policies.json changes, and custom browser CSS:

Extensions in Firefox and Notes on User Agency

I eventually want to reliably build Firefox extensions myself as part of an effort to harden my system's third party code sources, for now I grin and bear it and track changes to these as best as I can to keep changelogs in My RSS Feeds . I do keep a rule that the extensions that I use must be open source, so building and distributing them myself should at least be minimally feasible.

Right now, I use the following extensions:

Browser Features:

Page UX:

Privacy/Security:

These will be expanded on as I see necessary in to their own pages.

NEXT notes on user agency

this is kind of Zen and the art of Cringe below but lol...

about:config settings

svg.context-proprerties.content is used by the simple Tab Groups Addon and by Sidebery to access browser theme settings.

nix source: :noweb-ref profileConfig
"svg.context-properties.content.enabled" = true;

We turn on a bunch of privacy settings by default, like advanced tracking protection, the useless DNT header, and the ever so slightly less useless Global Privacy Control.

nix source: :noweb-ref profileConfig
"privacy.trackingprotection.enabled" = true; "privacy.donottrackheader.enabled" = true; "privacy.globalprivacycontrol.enabled" = true; "privacy.globalprivacycontrol.functionality_enabled" = true;

We disable some anti-features like the "Privacy Sandbox" attribution shit.

nix source: :noweb-ref profileConfig
"dom.private-attribution.submission.enabled" = false; "browser.newtabpage.activity-stream.showSponsored" = false; "browser.newtabpage.activity-stream.showSponsoredTopSites" = false;
nix source: :noweb-ref profileConfig
# for sidebery, remove unnecessary extra chrome https://old.reddit.com/r/FirefoxCSS/comments/1j5xv5q/how_can_i_get_rid_of_the_new_sidebar_and_also/ "sidebar.revamp" = true; "sidebar.verticalTabs" = true; "sidebar.visibility" = "expand-on-hover"; "sidebar.animation.expand-on-hover.duration-ms" = 100; "browser.ml.chat.hideLocalhost" = false; "browser.ml.chat.shortcuts" = false; "browser.ml.chat.provider" = ""; "browser.uidensity" = 1;
nix source: :noweb-ref profileConfig
"reader.color_scheme" = "dark"; "mousewheel.default.delta_multiplier_x" = 1000; # horizontal scroll multiplier
nix source: :noweb-ref profileConfig
"browser.aboutConfig.showWarning" = false; "browser.sessionstore.warnOnQuit" = true;

These settings enable userChrome and userContent, and also tell the browser to follow the system theme. the magic number 2 enumerates values in overrideContentColorScheme (see also StaticPrefList.yaml h/t Snow

nix source: :noweb-ref profileConfig
"toolkit.legacyUserProfileCustomizations.stylesheets" = true; "layout.css.prefers-color-scheme.content-override" = 2;

See also Privacy/Privacy Task Force/firefox about config privacy tweeks on MozillaWiki.

Disabling Policies with wrapped Firefox

per the docs, we can distribute a Firefox with enterprise policies enabled; you can enable extensions here, but I enable them in my user profile instead up above. The policies.json template that Mozilla maintains, as well as this page describing what each policy does, can be used to enable or disable a bunch of features that could be useful in running a kiosk or password-less couch computer.

  • Disable Firefox Home anti-features like Top Sites and the Sponsored stuff. Sorry homies.

  • Disable Firefox Studies which opt you in to browser behavior and web analytics studies sometimes to run research code in your web browser.

  • Disable onboarding and recommendation shit

  • Disable password management in lieu of vaultwarden

nix source: :noweb-ref myFirefox
pkgs.wrapFirefox pkgs.firefox-unwrapped { # nixExtensions = pkgs.callPackage ./firefox {}; # (ref:extensionsImport) nativeMessagingHosts = [ pkgs.kdePackages.plasma-browser-integration pkgs.tridactyl-native ]; extraPolicies = { DisableFirefoxStudies = true; DisablePocket = true; DisableTelemetry = true; FirefoxHome = { Pocket = false; Snippets = false; TopSites = false; Highlights = false; SponsoredTopSites = false; SponsoredPocket = false; }; UserMessaging = { ExtensionRecommendations = false; SkipOnboarding = true; }; OfferToSaveLoginsDefault = false; # Handlers can be set here for org-protocol # https://mozilla.github.io/policy-templates/#handlers }; }

Notice also that this Firefox also has native helpers for a few of the extensions that I listed above.

NEXT set up org-protocol handlers here

perhaps being able to set ask: false in here would be good? hm.

A Custom Firefox User Chrome

Approximately a zillion years ago I wrote:

Firefox recently made a transition from a programming system called XUL, and reimplemented a load of functionality in the browser as HTML and CSS running in Gecko and a new WebRender CSS renderer. This lead to performance and security boosts, as well as a much cleaner codebase, but it meant that a lot of customisation functionality was missing and a lot of mucking-around in the guts of firefox that some extensions would do was no longer possible.

Me, though, I'm happy with the work that Mozilla has been doing to implement a more powerful set of WebExtensions APIs than the ones that Google's Chrome team first implemented and standardized. I think it's a damned shame that Gecko and WebRenderer aren't easily embedded like that same Google team built with the Chrome Embedded Framework. I think much more interesting development on user-agents would be possible if Chromium was not the only embeddable option.

Boy howdy. I still think it's a damned shame. Oh well. That aside was attached to a listing of CSS customizations that I now include below which hide or tweak or customize certain parts of the UI to better work with my extensions. You used to be able to just "Inspect Element" on the browser chrome in one of the developer menus, but now I can't figure out how. There is, however, a few useful resources in understanding how to work with the browser DOM and style it, cascadingly.

Resources:

First off, I want to hide the tab toolbar since I'm using a vertical tab sidebar extension.

css source: :tangle ~/arroyo-nix/files/userChrome.css
/* This Source Code Form is subject to the terms of the Mozilla Public ,* License, v. 2.0. If a copy of the MPL was not distributed with this ,* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #TabsToolbar { visibility: collapse !important; } #nav-bar { /* Main Toolbar */ -moz-box-ordinal-group: 1 !important; } #PersonalToolbar { /* Bookmarks Toolbar */ -moz-box-ordinal-group: 2 !important; }

Make the default browser background dark instead of light. Why this isn't in the dark theme by default is beyond me.

css source: :tangle ~/arroyo-nix/files/userChrome.css
.browserContainer { background-color: var(--url-and-searchbar-background-color, hsla(0,0%,100%,.8)) !important; } #sidebar { background-color: white !important; } .sidebar-placesTree { background-color: white !important; }

Hide the header for the sidebar, and color the splitter to be the dark background color rather than light.

css source: :tangle ~/arroyo-nix/files/userChrome.css
/* #sidebar-header is hidden by default, change "none" to "inherit" to restore it. */ #sidebar-header { display: none !important; } /* #sidebar-splitter styles the divider between the sidebar and the rest of the browser. */ #sidebar-splitter { background-color: var(--url-and-searchbar-background-color, hsla(0,0%,10%,.8)) !important; }

This changes the new-tab and home page and whatnot to be dark.

css source: :tangle ~/arroyo-nix/files/userContent.css
/* ,* Description: Makes the activity stream page look decent with the dark theme. ,* ,* Screenshot: https://imgur.com/a/WcxGk ,* ,* Contributor(s): Andrei Cristian Petcu ,*/ @-moz-document url("about:home"), url("about:newtab") { .activity-stream { background-color: #474749 !important; } } @-moz-document url("about:blank") { ,*:empty:not([id]):not([style]) { background-color: #474749 !important; } }

Use Firefox in Emacs

emacs-lisp source: 
(provide 'cce/firefox-installation)

I use Firefox everywhere, except for YouTube Videos, as explained in Web Browsing , and here is how to use it in Emacs:

emacs-lisp source: 
;(setq browse-url-generic-program (executable-find "mpv")) ;; nothing to see here..... (set (if (> emacs-major-version 27) 'browse-url-handlers 'browse-url-browser-function) '(;; ("https?://\\(www.\\)?youtu.?be\\(.com\\)?/" . browse-url-generic) ("." . browse-url-firefox)))

Web Philosophy

WAITING Theoretically-Safe Firefox Extensions with Nix

I have this disabled right now ... it's likely easier to just use Firefox Sync for this since the XPIs are just being downloaded

So, Home Manager has the ability to manager a Firefox profile and in doing so, it can side-load extensions which are compiled and managed by Nix. Very nice.

It's recommended in the home-configuration.nix manpage to pull firefox addons out of roam:Nix User Repositories (external: NUR) but i'm not sure i'm comfortable running NUR as an integrated part of my system, I only want to use the minimal set of it. So my understanding of NUR's support for Firefox addons looks like this: addons.json provide an accessible format for machines to generate a generated-firefox-addons.nix with a script like this generate-firefox-addons.sh which also makes a nixlib/firefox/default.nix for importing at (extensionsImport).

 generate-firefox-addonssh source: :tangle ~/arroyo-nix/firefox/generate-firefox-addons.sh :mkdirp yes :shebang #!/usr/bin/env bash
nix run -f "https://github.com/nix-community/NUR/archive/master.tar.gz" repos.rycee.firefox-addons-generator \ --arg pkgs 'import <nixpkgs> {}' \ -c nixpkgs-firefox-addons addons.json ./generated.nix

Regularly running shell:pushd nixlib/firefox && ./generate-firefox-addons.sh & is "praxis".

default.nix looks like:

nix source: :tangle ~/arroyo-nix/firefox/default.nix
# Derived from: https://github.com/nix-community/nur-combined/blob/master/repos/ijohanne/pkgs/firefox-plugins/default.nix { pkgs ? import <nixpkgs> {} }: let buildFirefoxXpiAddon = { pname, url, sha256, meta, ... }: let newMeta = if meta ? license then meta else meta // { license = pkgs.lib.licenses.unfree; }; in (pkgs.fetchFirefoxAddon { inherit url sha256; name = pname; }) // { meta = newMeta;} ; generated = pkgs.callPackage ./generated.nix { inherit buildFirefoxXpiAddon; }; in pkgs.lib.mapAttrsToList (name: value: value) (pkgs.lib.filterAttrs # (ref:firefox-filterAttrs) (name: value: (name != "override") && (name != "overrideDerivation") && (name != "recurseForDerivations")) (generated))

The most complicated part of this is the map and filter functions in (firefox-filterAttrs) above: programs.firefox.extensions wants a list, the generated addons module returns a set with some overrides attached to it. I just want this to rip 'em out and map it to a list.

And here's an addons.json specifying some of my favorite slugs:

json source: :tangle ~/arroyo-nix/firefox/addons.json :comments none
[ {"slug": "awesome-rss"}, {"slug": "better-timing-for-no-race"}, {"slug": "browserpass-ce"}, {"slug": "clearurls"}, {"slug": "darkreader"}, {"slug": "decentraleyes"}, {"slug": "duckduckgo-for-firefox"}, {"slug": "facebook-container"}, {"slug": "https-everywhere"}, {"slug": "laboratory-by-mozilla"}, {"slug": "leechblock-ng"}, {"slug": "multi-account-containers"}, {"slug": "plasma-integration"}, {"slug": "privacy-badger17"}, {"slug": "simple-tab-groups"}, {"slug": "smart-amazon-smile"}, {"slug": "styl-us"}, {"slug": "tab-unload-for-tree-style-tab"}, {"slug": "tampermonkey"}, {"slug": "tree-style-tab"}, {"slug": "twemex-sidebar-for-twitter"}, {"slug": "ublock-origin"}, {"slug": "wallabagger"}, {"slug": "web-scrobbler"}, {"slug": "yet-another-hints-extension"}, {"slug": "youtube-feeds"} ]

This still fetches and repackages the XPIs! I'd like to build these more and more from source ... but they won't auto-update to something that'll super-own me for now. See An Undeveloped Thought on Web Extensions and the untenable position of powerful user agents below...

WAITING Add changelogs or git commits for these extensions to Universal Aggregator Software Release Feeds

  • State "WAITING" from "NEXT" [2021-05-11 Tue 12:05]

An Undeveloped Thought on Web Extensions and the untenable position of powerful user agents

With regards to the security model of web extensions:

I really don't think the browser extension model is great, but I think user-agents are powerful and powerful user agents are good. There has to be a balance. It's clear that the cloud-services-as-extension model like Grammarly or even AdBlock is ripe for abuse, or change of terms, and the nature of the ecosystem is that when open source developers burn out they can turn around and sell their module to a firm with no trust, and plenty of things to scrutinize.

When subscribing to the changelogs, I see limited authorship information as well as commit patterns: if the module suddenly changes authorship or the commits change from being a very verbose style to only dumping version updates, I need to ask why before it's too late.

The NUR stuff is all based around some automation in that repo which is just managed ad-hoc by a bunch of folks as their personal playgrounds, but I think I can co-opt that and define my own extension set using the same pattern. If I developed that way, this would just be a readme in a forked NUR repo, but here we are, I want to speak through the code, sorry.

By only managing and installing the core open source extensions I rely on, maybe I can keep myself from getting roam:Super Owned by some extension getting sold to some amoral surveillance apparatus, while managing the risk of security issues injected by the relatively lax NUR security model. The extensions I use largely don't interact with cloud services or even update very often themselves, and it's as easy to track the commit feeds or changelog for them in my News feed pipeline .

Another Undeveloped Thought: Zen Browser and the art of Cringe

As an aside, I tried out a GitHub project Firefox fork name of Zen Browser, and it's really neat in that it exposes a lot of what I have built above by default while still maintaining full compatibility with the Add-Ons ecosystem and Firefox Sync too...

But the thing is, and I'm only starting to become "old man yells at cloud" about this now, it's also extremely tasteless? There is a certain smell to certain types of GitHub projects like this, the modern day funroll-loops. A thing that seeped out of /r/unixporn and the Hyprland started calling "ironically reclaiming" this by referring to their highly aesthetically customized tiling Linux pick-and-play environment as "rices" which I think is at the very least in immature, poor taste and ultimately basically comes from people being racist or at least racialist edgelords more than fifteen years ago.

So anyways I was playing with Zen and looking around for the button to make it the default browser, because I wanted to click links and have them open in there for a while after i got it set up close enough to how I like my Firefox above, but still using the Zen features instead of e.g. Sidebery.

It's basically a Firefox with a UserChrome that overhauls the tab bar, along with a vertical tab setup that has both a Workspace/Tab Group feature as well as vertical tabs. Some other features. Not clear if there is any particular privacy features or toggles that Firefox ships or could ship as above. And I think that is fine! I think it's a bummer that folks aren't more empowered to deploy their own configurations, but I recognize that I am slightly unwell!

But here's the thing, people still go through all this rigamarole with it, to the point where I found a menu item called "Share Zen Rice"

It was cringey enough that I am now refreshing this page and making sure that new things that are anti-features are disabled or at least evaluated. I have changelogs for policies.json template changes, etc. It feels better to have this steady chore instead of less angry bursts of forced work, but YMMV. If that doesn't resonate with you, it's likely that none of the CCE stuff does, and that's okay.