Can someone help me with my firefox.nix?

So first everything worked great. I got Firefox installed, with the right extensions and the right settings. Awesome.

Then I wanted to do one more small thing: I wanted to correctly set my userChrome.css and userContent.css files. Then my trouble started. Apparently this can only be done through importing the firefox.nix through the home-manager file home.nix

Now I changed my firefox.nix around and imported it in my home.nix and now the rebuild crashes and I absolutely don’t get why.

This is the import in my home.nix:

imports = [
    ./firefox.nix
  ];

The error I’m getting is:

Sep 20 14:08:11 nixos hm-activate-user[2065]: - Move or remove the files below and try again.
Sep 20 14:08:11 nixos hm-activate-user[2065]: - In standalone mode, use 'home-manager switch -b backup' to back up
Sep 20 14:08:11 nixos hm-activate-user[2065]:   files automatically.
Sep 20 14:08:11 nixos hm-activate-user[2065]: - When used as a NixOS or nix-darwin module, set
Sep 20 14:08:11 nixos hm-activate-user[2065]:     'home-manager.backupFileExtension'
Sep 20 14:08:11 nixos hm-activate-user[2065]:   to, for example, 'backup' and rebuild.
Sep 20 14:08:11 nixos hm-activate-user[2065]: Existing file '/home/user/.mozilla/firefox/profiles.ini' would be clobbered
Sep 20 14:08:11 nixos systemd[1]: home-manager-user.service: Main process exited, code=exited, status=1/FAILURE
Sep 20 14:08:11 nixos systemd[1]: home-manager-user.service: Failed with result 'exit-code'.
Sep 20 14:08:11 nixos systemd[1]: Failed to start Home Manager environment for user.

This is my firefox.nix:

{ config, pkgs, ... }:

  let
    lock-false = {
      Value = false;
      Status = "locked";
    };
    lock-true = {
      Value = true;
      Status = "locked";
    };
  in
{
  programs = {
    firefox = {
      enable = true;
      languagePacks = [ "en-US" ];
      profiles = {
        default = {
          id = 0;
          name = "regen";
          isDefault = true;
          userChrome = (builtins.readFile ./das_Dotfile/profiles/firefox/userChrome.css);
          userContent = (builtins.readFile ./das_Dotfile/profiles/firefox/userContent.css);
        };
      };

      /* ---- POLICIES ---- */
      # Check about:policies#documentation for options.
      policies = {
        DisableTelemetry = true;
        DisableFirefoxStudies = true;
        DisablePocket = true;
        DisplayBookmarksToolbar = "always"; # alternatives: "never" or "newtab"

        /* ---- EXTENSIONS ---- */
        # Check about:support for extension/add-on ID strings.
        # Valid strings for installation_mode are "allowed", "blocked",
        # "force_installed" and "normal_installed".
        ExtensionSettings = {
          "*".installation_mode = "allowed"; # blocks all addons except the ones specified below
          # uBlock Origin
          "uBlock0@raymondhill.net" = {
            install_url = "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi";
            installation_mode = "force_installed";
          };
          # Firefox Relay
          "private-relay@firefox.com" = {
            install_url = "https://addons.mozilla.org/firefox/downloads/latest/private-relay/latest.xpi";
            installation_mode = "force_installed";
          };
          # Bitwarden
          "{446900e4-71c2-419f-a6a7-df9c091e268b}" = {
            install_url = "https://addons.mozilla.org/firefox/downloads/latest/bitwarden-password-manager/latest.xpi";
            installation_mode = "force_installed";
          };
          # I don't care about cookies
          "jid1-KKzOGWgsW3Ao4Q@jetpack" = {
            install_url = "https://addons.mozilla.org/firefox/downloads/latest/i-dont-care-about-cookies/latest.xpi";
            installation_mode = "force_installed";
          };
          # Sidebery
          "{3c078156-979c-498b-8990-85f7987dd929}" = {
            install_url = "https://addons.mozilla.org/firefox/downloads/latest/sidebery/latest.xpi";
            installation_mode = "force_installed";
          };
        };
  
        /* ---- PREFERENCES ---- */
        # Check about:config for options.
        Preferences = {
          # Convenience
          "extensions.pocket.enabled" = lock-false;
          "browser.formfill.enable" = lock-false;
          "browser.search.suggest.enabled" = lock-false;
          "browser.search.suggest.enabled.private" = lock-false;
          "browser.urlbar.suggest.searches" = lock-false;
          "browser.urlbar.showSearchSuggestionsFirst" = lock-false;
          "signon.rememberSignons" = lock-false; # Stop asking to save passwords
          "extensions.formautofill.addresses.capture.enabled" = lock-false; # Stop asking to save addresses
          # Styling
          "browser.compactmode.show" = lock-true;
          "toolkit.legacyUserProfileCustomizations.stylesheets" = lock-true; # This is needed for other userX.css files
          # Containers
          "privacy.userContext.enabled" = lock-true;
          "privacy.userContext.ui.enabled" = lock-true;
          # Downloads
          "browser.download.useDownloadDir" = lock-false;
          "browser.download.always_ask_before_handling_new_types" = lock-true;
          # Privacy
          "privacy.sanitize.sanitizeOnShutdown" = lock-true;
          "privacy.clearOnShutdown_v2.cache" = lock-true;
          "privacy.clearOnShutdown_v2.historyFormDataAndDownloads" = lock-true;
          "privacy.clearOnShutdown_v2.browsingHistoryAndDownloads" = lock-true;
          "privacy.clearOnShutdown_v2.downloads" = lock-true;
          "privacy.clearOnShutdown_v2.formdata" = lock-true;
          "privacy.clearOnShutdown_v2.cookiesAndStorage" = lock-true;
          # HTTPS only
          "dom.security.https_only_mode" = lock-true;
        };
      };
    };
  };
}

I’d really appreciate any tips.

Did you read the error? It tells you how to fix this.

2 Likes

Fair enough. My interpretation is that I shall delete the file “/home/user/.mozilla/firefox/profiles.ini” and then try again to rebuild.

I’ll try that later but why would I even need to do that manually.

Right now that file has been managed imperatively without Home Manager and from Home Manager’s perspective could have anything in it. If Home Manager deletes the file itself to then start managing it, important data might be lost. That’s why you have to make the choice to delete it yourself.

5 Likes

You guys really know your stuff. Thanks so much for helping me. Now everything works. I did select my answer as the solution because it technically is. But thanks Mr. Waffle for the great pointer!

You can use programs.firefox.autoConfig to setup userChrome.css without home-manager:

{
  config,
  lib,
  pkgs,
  ...
}:

{
  programs.firefox.autoConfig = ''
    try {
      let sss = Components.classes["@mozilla.org/content/style-sheet-service;1"].getService(Components.interfaces.nsIStyleSheetService);
      let uri = Services.io.newURI("file://${./userChrome.css}", sss.USER_SHEET);
      if (!sss.sheetRegistered(uri, sss.USER_SHEET)) {
        sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
      }
    } catch(ex){
      Components.utils.reportError(ex.message);
    }
  '';
}
6 Likes

Thanks! But the home-manager approach works well now. Also the readability is better in the home-manager approach and I can add profiles as well.

This is great! I always prefer to do things in the Nix store if possible since it seems more “pure” than using home-manager. Is there a similar hack for userContent.css?

Unfortunately it also looks like nsIStyleSheetService apples to both the browser UI and web content, so loading firefox-gnome-theme this way breaks some website UI’s. I’m still trying to find a way to do this.

I think you should use this for browser UI:

@-moz-document url(chrome://browser/content/browser.xhtml) {
}

and this for website:

@-moz-document url-prefix("https://...") {
}

Sorry if this is a dumb question (I have very little knowledge of CSS), but right now my userChrome.css file contains

@import "${inputs.firefox-gnome-theme}/userChrome.css";

How would I put this inside of @-moz-document url(chrome://browser/content/browser.xhtml)? I tried

@-moz-document url(chrome://browser/content/browser.xhtml) {
  @import "${inputs.firefox-gnome-theme}/userChrome.css";
}

but this did not work (I think that @imports cannot appear inside of the @-moz-document scope block, since they have to be at the top of the file).

Copying the contents of ${inputs.firefox-gnome-theme}/userChrome.css into the scope does not fix the problem unfortunately, since this module is also heavily organized into imports. Maybe there is a way to recursively inline these @imports at build time so that the entire thing can be wrapped with the @-moz-document block—do you know of any such tool?

Try:

@-moz-document url(chrome://browser/content/browser.xhtml) {
  @import url("file://${inputs.firefox-gnome-theme}/userChrome.css");
}

Edit: After reading https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@import, I dont’ think it will work.

Yep, I spent a bit of effort trying to get this to work but I haven’t been able to. I’m still not entirely sure why the

@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");

directive in all of the provided firefox-gnome-theme CSS files is still not sufficient to prevent it from styling webpages. It looks like @import can be conditional on @media queries, but I haven’t found one that would have the same effect as @-moz-document. I want to make sure that there is no CSS solution before resorting to some ugly patchPhase hack on firefox-gnome-theme.

I finally found a hack that solves the problem, courtesy of the very nice people in the @firefoxcss:mozilla.org Matrix. The following is a complete snippet that will install firefox-gnome-theme without needing to use home-manager

programs.firefox.autoConfig = ''
  // Load userContent.css
  try {
    let sss = Components.classes["@mozilla.org/content/style-sheet-service;1"].getService(Components.interfaces.nsIStyleSheetService);
    let uri = Services.io.newURI("file://${pkgs.firefox-gnome-theme}/share/firefox-gnome-theme/userContent.css", sss.AGENT_SHEET);
    if (!sss.sheetRegistered(uri, sss.AGENT_SHEET)) {
      sss.loadAndRegisterSheet(uri, sss.AGENT_SHEET);
    }
  } catch(ex){
    Components.utils.reportError("Loading userContent failed");
  }

  // Load userChrome.css
  const lazy = {};
  function addSheet(win){
    try {
      let uri = Services.io.newURI("file://${pkgs.firefox-gnome-theme}/share/firefox-gnome-theme/userChrome.css");
      if( !lazy.preloadedStyle ){
        let sss = Components.classes["@mozilla.org/content/style-sheet-service;1"].getService(Components.interfaces.nsIStyleSheetService);
        lazy.preloadedStyle = sss.preloadSheet(uri, sss.USER_SHEET);
      }
      win.addEventListener("DOMContentLoaded",() => {
        win.windowUtils.addSheet(lazy.preloadedStyle, Components.interfaces.nsIDOMWindowUtils.USER_SHEET);
      }, { once:true })

    } catch(ex){
      Components.utils.reportError(ex);
    }
  }
  const observer = (aSubject) => {
    if( aSubject.isChromeWindow ){
      addSheet(aSubject)
    }
  }
  Services.obs.addObserver(observer, 'domwindowopened', false);
'';