1
0
mirror of https://github.com/nix-community/home-manager synced 2024-06-14 02:33:38 +02:00

firefox: add librewolf support

This adds LibreWolf support and tests, including using the correct
profile path and generating the correct search json hash.

The way Firefox (& forks) profiles work on Linux is you have ~/.mozilla
for some more "global" stuff like native messaging hosts, and
~/.<vendor>/<appName> for more "local" stuff like profiles or crash
reports. However, if vendor isn't set, ~/.appName is used instead.
Additionally, Firefox forks usually change ~/.mozilla to match
~/.<vendor>.

This is a huge mess so I don't expect it to work for every Firefox fork
out there - instead, I only provide support for the browser I personally
use, and if anyone wishes to add another one, they'd fine it relatively
easy to do now that all you need to do is make sure the values at the
top are consistent with where the browser stores its data.

Some of it could perhaps be moved to nixpkgs, but, again, considering
how messy this *can* be (I don't know how bad things are in practice),
this may or may not be a good idea.
This commit is contained in:
chayleaf 2024-02-06 14:33:09 +07:00
parent 6d3b6dc922
commit 2a477b78f5
No known key found for this signature in database
GPG Key ID: 78171AD46227E68E
5 changed files with 131 additions and 23 deletions

View File

@ -10,13 +10,33 @@ let
jsonFormat = pkgs.formats.json { };
mozillaConfigPath =
if isDarwin then "Library/Application Support/Mozilla" else ".mozilla";
# see application.ini's "Vendor" and "Name" properties
# this can be parsed with IFD, but obviously it's better to avoid IFD
# XXX: is it better to push the maintenance of this list into nixpkgs?
vendor = {
librewolf = null;
}.${cfg.package.binaryName or lib.getName cfg.package} or "Mozilla";
appName = {
librewolf = "LibreWolf";
}.${cfg.package.binaryName or lib.getName cfg.package} or "Firefox";
vendorLower = if vendor != null then lib.toLower vendor else null;
appNameLower = lib.toLower appName;
vendorOrApp = if vendor != null then vendor else appName;
vendorOrAppLower = lib.toLower vendorOrApp;
mozillaConfigPath = if isDarwin then
"Library/Application Support/${vendorOrApp}"
else
".${vendorOrAppLower}";
firefoxConfigPath = if isDarwin then
"Library/Application Support/Firefox"
"Library/Application Support/${appName}"
else if vendorLower != null then
"${mozillaConfigPath}/${appNameLower}"
else
"${mozillaConfigPath}/firefox";
mozillaConfigPath;
profilesPath =
if isDarwin then "${firefoxConfigPath}/Profiles" else firefoxConfigPath;
@ -193,11 +213,10 @@ let
fcfg = { enableGnomeExtensions = cfg.enableGnomeExtensions; };
# A bit of hackery to force a config into the wrapper.
browserName =
package.browserName or (builtins.parseDrvName package.name).name;
applicationName = package.binaryName or lib.getName package;
# The configuration expected by the Firefox wrapper builder.
bcfg = setAttrByPath [ browserName ] fcfg;
bcfg = setAttrByPath [ applicationName ] fcfg;
in if package == null then
null
@ -212,7 +231,12 @@ let
(pkgs.wrapFirefox.override { config = bcfg; }) package { };
in {
meta.maintainers = [ maintainers.rycee maintainers.kira-bruneau ];
meta.maintainers = [
maintainers.rycee
maintainers.kira-bruneau
# LibreWolf-specific
maintainers.chayleaf
];
imports = [
(mkRemovedOptionModule [ "programs" "firefox" "extensions" ] ''
@ -896,22 +920,22 @@ in {
# maliciously. We're modifying the search outside of Firefox, but
# a claim by Mozilla to remove this would be very anti-user, and
# is unlikely to be an issue for our use case.
disclaimer = appName:
"By modifying this file, I agree that I am doing so "
+ "only within ${appName} itself, using official, user-driven search "
# Use appName from application.ini as this can safely be detected
# without using an IFD.
disclaimer = "By modifying this file, I agree that I am doing so "
+ "only within $appName itself, using official, user-driven search "
+ "engine selection processes, and in a way which does not circumvent "
+ "user consent. I acknowledge that any attempt to change this file "
+ "from outside of ${appName} is a malicious act, and will be responded "
+ "from outside of $appName is a malicious act, and will be responded "
+ "to accordingly.";
salt = if profile.search.default != null then
profile.path + profile.search.default + disclaimer "Firefox"
profile.path + profile.search.default + disclaimer
else
null;
privateSalt = if profile.search.privateDefault != null then
profile.path + profile.search.privateDefault
+ disclaimer "Firefox"
profile.path + profile.search.privateDefault + disclaimer
else
null;
in pkgs.runCommand "search.json.mozlz4" {
@ -920,8 +944,9 @@ in {
inherit salt privateSalt;
} ''
if [[ -n $salt ]]; then
export hash=$(echo -n "$salt" | openssl dgst -sha256 -binary | base64)
export privateHash=$(echo -n "$privateSalt" | openssl dgst -sha256 -binary | base64)
appName="$(sed '/^Name=/!d;s/Name=//' ${cfg.finalPackage}/lib/*/application.ini)"
hash=$(echo -n "$salt" | sed "s=\$appName=$appName=g" | openssl dgst -sha256 -binary | base64)
privateHash=$(echo -n "$privateSalt" | sed "s=\$appName=$appName=g" | openssl dgst -sha256 -binary | base64)
mozlz4a <(substituteStream json search.json.in --subst-var hash --subst-var privateHash) "$out"
else
mozlz4a <(echo "$json") "$out"

View File

@ -6,4 +6,5 @@
firefox-duplicate-container-ids = ./duplicate-container-ids.nix;
firefox-container-id-out-of-range = ./container-id-out-of-range.nix;
firefox-policies = ./policies.nix;
firefox-librewolf = ./librewolf.nix;
}

View File

@ -0,0 +1,58 @@
{ config, lib, pkgs, ... }:
{
imports = [ ./setup-firefox-mock-overlay.nix ];
config = lib.mkIf config.test.enableBig {
home.stateVersion = "23.11";
programs.firefox = {
enable = true;
package = pkgs.librewolf;
profiles.basic.isDefault = true;
profiles.test = {
id = 1;
settings = {
"general.smoothScroll" = false;
"browser.newtabpage.pinned" = [{
title = "NixOS";
url = "https://nixos.org";
}];
};
search = {
force = true;
default = "DuckDuckGo";
privateDefault = "DuckDuckGo";
};
};
};
nmt.script = ''
assertFileRegex \
home-path/bin/librewolf \
MOZ_APP_LAUNCHER
assertDirectoryExists home-files/.librewolf/basic
assertFileContent \
home-files/.librewolf/test/user.js \
${./profile-settings-expected-user.js}
function assertFirefoxSearchContent() {
compressedSearch=$(normalizeStorePaths "$1")
decompressedSearch=$(dirname $compressedSearch)/search.json
${pkgs.mozlz4a}/bin/mozlz4a -d "$compressedSearch" >(${pkgs.jq}/bin/jq . > "$decompressedSearch")
assertFileContent \
$decompressedSearch \
"$2"
}
assertFirefoxSearchContent \
home-files/.librewolf/test/search.json.mozlz4 \
${./profile-settings-expected-search-with-librewolf.json}
'';
};
}

View File

@ -0,0 +1,17 @@
{
"engines": [
{
"_isAppProvided": true,
"_metaData": {},
"_name": "DuckDuckGo"
}
],
"metaData": {
"current": "DuckDuckGo",
"hash": "ZIx5PXpJILB0NZQARse1Y6PRrlroZEb6VMvu1FY2sow=",
"private": "DuckDuckGo",
"privateHash": "ZIx5PXpJILB0NZQARse1Y6PRrlroZEb6VMvu1FY2sow=",
"useSavedOrder": false
},
"version": 6
}

View File

@ -7,16 +7,23 @@
meta.description = "I pretend to be Firefox";
passthru.gtk3 = null;
} ''
mkdir -p "$out"/{bin,lib}
mkdir -p "$out"/{bin,lib/firefox}
touch "$out/bin/firefox"
chmod 755 "$out/bin/firefox"
echo "Name=Firefox" > "$out/lib/firefox/application.ini"
'';
chrome-gnome-shell =
pkgs.runCommandLocal "dummy-chrome-gnome-shell" { } ''
mkdir -p $out/lib/mozilla/native-messaging-hosts
touch $out/lib/mozilla/native-messaging-hosts/dummy
'';
librewolf-unwrapped = pkgs.runCommandLocal "librewolf-0" {
meta.description = "I pretend to be LibreWolf";
passthru.gtk3 = null;
passthru.extraPrefsFiles = null;
passthru.extraPoliciesFiles = null;
} ''
mkdir -p "$out"/{bin,lib/librewolf}
touch "$out/bin/librewolf"
chmod 755 "$out/bin/librewolf"
echo "Name=LibreWolf" > "$out/lib/librewolf/application.ini"
'';
})
];
}