{ pkgs, lib }: with lib; rec { mozlz4 = pkgs.callPackage ./mozlz4.nix {}; flattenAttrs = set: let recurse = k: v: if isAttrs v then mapAttrsToList (k': recurse "${k}.${k'}") v else nameValuePair k v; in listToAttrs (flatten (mapAttrsToList recurse set)); mkValueString = v: if isInt v then toString v else if isString v then "\"${v}\"" else if true == v then "true" else if false == v then "false" else abort "unsupported value type: ${builtins.typeOf v}"; mkPrefs = settings: pkgs.writeText "prefs.js" ("// dummy line\n" + (lib.concatStringsSep "\n" (lib.mapAttrsToList (k: v: "pref(\"${k}\", ${mkValueString v}, locked);") (flattenAttrs settings)))); toSearchConfig = settings: pkgs.runCommand "search.json.mozlz4" {} '' ${mozlz4.compress} < ${pkgs.writeText "search.json" (builtins.toJSON settings)} > $out ''; profile = { preferences, search }: pkgs.runCommand "profile" {} '' mkdir $out ln -s ${toUserPrefs preferences} $out/user.js # ln -s ${toSearchConfig search} $out/search.json.mozlz4 cp ${../search-with-qj.json.mozlz4} $out/search.json.mozlz4 ''; mkPolicies = policies: pkgs.writeText "policies.json" (builtins.toJSON { inherit policies; }); mergeProfiles = profiles: let sanitise = args: { policies = args.policies or {}; preferences = args.preferences or {}; }; sanitised = map sanitise profiles; final = lib.foldl lib.recursiveUpdate {} sanitised; in final; patchOmniJa = src: script: pkgs.runCommand "omni.ja" { } '' ${pkgs.unzip}/bin/unzip -q ${src} ${script} ${pkgs.zip}/bin/zip -qr9XD $out . ''; bundle = { policies ? {}, preferences ? {}, patchOmniJaCommand ? "" }: let firefox = pkgs.firefox-unwrapped; policies' = mkPolicies policies; preferences' = mkPrefs preferences; patchedOmniJa = patchOmniJa "${firefox}/lib/firefox/browser/omni.ja" patchOmniJaCommand; patched = pkgs.runCommand "firefox-bundle" { nativeBuildInputs = [ pkgs.nix ]; disallowedReferences = [ firefox ]; } '' cp -r ${firefox} $out chmod -R +w $out # correct argv[0], which is used to locate distribution and defaults substituteInPlace $out/bin/firefox \ --replace ${firefox} $out mkdir $out/lib/firefox/distribution cp ${policies'} $out/lib/firefox/distribution/policies.json cp ${preferences'} $out/lib/firefox/defaults/pref/99-custom.js cp ${patchedOmniJa} $out/lib/firefox/browser/omni.ja ''; wrapped = (pkgs.wrapFirefox patched { browserName = "firefox"; version = "custom"; }) // { inherit policies preferences; }; in wrapped; # This attempts to provide a start for deploying Nix-configured profiles to # systems which do not have Nix installed, and where /nix/store is undesired. # It does this by bundling all referenced store items into $out, and rewriting # references to those store items. export = { selfPath, policies ? {}, preferences ? {} }: pkgs.stdenv.mkDerivation rec { name = "firefox-profile-export"; nativeBuildInputs = [ pkgs.jq ]; allowedRequisites = []; buildCommand = let policyFile = mkPolicies policies; prefsFile = mkPrefs preferences; closure = pkgs.closureInfo { rootPaths = [ policyFile prefsFile ]; }; in '' mkdir $out $out/store storePaths=$(cat ${closure}/store-paths) for p in $storePaths; do cp -a "$p" $out/store/"$(basename "$p" | sed -e 's|\([a-z0-9]\{32\}\)-||')" done mv $out/store/prefs.js $out/ jq < $out/store/policies.json > $out/policies.json find $out -type f -print0 | xargs -0I{} -- sed -i -e "s|$NIX_STORE/\\([a-z0-9]\{32\}\\)-|${selfPath}/store/|g" "{}" ''; }; launcher = firefox: pkgs.writeShellScriptBin "firefox" '' # FF doesn't accept ro profiles, tries to create lockfile TMP_PROFILE="$(mktemp -d)" echo "$TMP_PROFILE" function finish() { echo deleting profile # rm -rv "$TMP_PROFILE" } trap finish EXIT # cp -r ''${profile} "$TMP_PROFILE" chmod u+rwx -R "$TMP_PROFILE" export MOZ_LOG=nsHttp:1 export MOZ_LOG_FILE=ff # Make FF pay attention to services.settings.server # https://bugzilla.mozilla.org/show_bug.cgi?id=1598562 export XPCSHELL_TEST_PROFILE_DIR=1 ${firefox}/bin/firefox \ --no-remote \ --profile "$TMP_PROFILE" \ "$@" ''; }