mirror of
https://github.com/nix-community/home-manager.git
synced 2025-11-08 19:46:05 +01:00
This commit refactors programs.firefox.profiles.<name>.extensions in order to support both installation of extensions (addons) and their configuration. It does this by setting the `extensions.webextensions.ExtensionStorageIDB.enabled` user_pref to false. When this preference is set to false, support for storing extension settings in sqlite databases, also known as IndexedDB or IDB, is reverted back to the JSON format present in firefox versions prior to version 63, as seen here: https://blog.mozilla.org/addons/2018/08/03/new-backend-for-storage-local-api/ IndexedDB was made the default due to performance improvements, but had the consequence of removing any possibility of declarative extension configuration without the assistance of firefox's policy system. The policy system is supported by a small amount of extensions, such as uBlock Origin, but has to be explicitly supported. Even when supported, it provides significantly less granular control when compared to the JSON storage format.
918 lines
31 KiB
Nix
918 lines
31 KiB
Nix
{ modulePath, name, description ? null, wrappedPackageName ? null
|
|
, unwrappedPackageName ? null, platforms, visible ? false
|
|
, enableBookmarks ? true, }:
|
|
{ config, lib, pkgs, ... }:
|
|
with lib;
|
|
let
|
|
inherit (pkgs.stdenv.hostPlatform) isDarwin;
|
|
|
|
appName = name;
|
|
|
|
moduleName = concatStringsSep "." modulePath;
|
|
|
|
cfg = getAttrFromPath modulePath config;
|
|
|
|
jsonFormat = pkgs.formats.json { };
|
|
|
|
supportedPlatforms = flatten (attrVals (attrNames platforms) lib.platforms);
|
|
|
|
isWrapped = versionAtLeast config.home.stateVersion "19.09"
|
|
&& wrappedPackageName != null;
|
|
|
|
defaultPackageName =
|
|
if isWrapped then wrappedPackageName else unwrappedPackageName;
|
|
|
|
packageName = if wrappedPackageName != null then
|
|
wrappedPackageName
|
|
else
|
|
unwrappedPackageName;
|
|
|
|
profilesPath =
|
|
if isDarwin then "${cfg.configPath}/Profiles" else cfg.configPath;
|
|
|
|
nativeMessagingHostsPath = if isDarwin then
|
|
"${cfg.vendorPath}/NativeMessagingHosts"
|
|
else
|
|
"${cfg.vendorPath}/native-messaging-hosts";
|
|
|
|
nativeMessagingHostsJoined = pkgs.symlinkJoin {
|
|
name = "ff_native-messaging-hosts";
|
|
paths = [
|
|
# Link a .keep file to keep the directory around
|
|
(pkgs.writeTextDir "lib/mozilla/native-messaging-hosts/.keep" "")
|
|
# Link package configured native messaging hosts (entire browser actually)
|
|
cfg.finalPackage
|
|
]
|
|
# Link user configured native messaging hosts
|
|
++ cfg.nativeMessagingHosts;
|
|
};
|
|
|
|
# The extensions path shared by all profiles; will not be supported
|
|
# by future browser versions.
|
|
extensionPath = "extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
|
|
|
|
profiles = flip mapAttrs' cfg.profiles (_: profile:
|
|
nameValuePair "Profile${toString profile.id}" {
|
|
Name = profile.name;
|
|
Path = if isDarwin then "Profiles/${profile.path}" else profile.path;
|
|
IsRelative = 1;
|
|
Default = if profile.isDefault then 1 else 0;
|
|
}) // {
|
|
General = {
|
|
StartWithLastProfile = 1;
|
|
} // lib.optionalAttrs (cfg.profileVersion != null) {
|
|
Version = cfg.profileVersion;
|
|
};
|
|
};
|
|
|
|
profilesIni = generators.toINI { } profiles;
|
|
|
|
userPrefValue = pref:
|
|
builtins.toJSON (if isBool pref || isInt pref || isString pref then
|
|
pref
|
|
else
|
|
builtins.toJSON pref);
|
|
|
|
mkUserJs = prePrefs: prefs: extraPrefs: bookmarks: extensions:
|
|
let
|
|
prefs' = lib.optionalAttrs ([ ] != bookmarks) {
|
|
"browser.bookmarks.file" = toString (browserBookmarksFile bookmarks);
|
|
"browser.places.importBookmarksHTML" = true;
|
|
} // lib.optionalAttrs (extensions != { }) {
|
|
"extensions.webextensions.ExtensionStorageIDB.enabled" = false;
|
|
} // prefs;
|
|
in ''
|
|
// Generated by Home Manager.
|
|
|
|
${prePrefs}
|
|
|
|
${concatStrings (mapAttrsToList (name: value: ''
|
|
user_pref("${name}", ${userPrefValue value});
|
|
'') prefs')}
|
|
|
|
${extraPrefs}
|
|
'';
|
|
|
|
mkContainersJson = containers:
|
|
let
|
|
containerToIdentity = _: container: {
|
|
userContextId = container.id;
|
|
name = container.name;
|
|
icon = container.icon;
|
|
color = container.color;
|
|
public = true;
|
|
};
|
|
in ''
|
|
${builtins.toJSON {
|
|
version = 5;
|
|
lastUserContextId =
|
|
foldlAttrs (acc: _: value: if value.id > acc then value.id else acc) 0
|
|
containers;
|
|
identities = mapAttrsToList containerToIdentity containers ++ [
|
|
{
|
|
userContextId = 4294967294; # 2^32 - 2
|
|
name = "userContextIdInternal.thumbnail";
|
|
icon = "";
|
|
color = "";
|
|
accessKey = "";
|
|
public = false;
|
|
}
|
|
{
|
|
userContextId = 4294967295; # 2^32 - 1
|
|
name = "userContextIdInternal.webextStorageLocal";
|
|
icon = "";
|
|
color = "";
|
|
accessKey = "";
|
|
public = false;
|
|
}
|
|
];
|
|
}}
|
|
'';
|
|
|
|
browserBookmarksFile = bookmarks:
|
|
let
|
|
indent = level:
|
|
lib.concatStringsSep "" (map (lib.const " ") (lib.range 1 level));
|
|
|
|
bookmarkToHTML = indentLevel: bookmark:
|
|
''
|
|
${indent indentLevel}<DT><A HREF="${
|
|
escapeXML bookmark.url
|
|
}" ADD_DATE="1" LAST_MODIFIED="1"${
|
|
lib.optionalString (bookmark.keyword != null)
|
|
" SHORTCUTURL=\"${escapeXML bookmark.keyword}\""
|
|
}${
|
|
lib.optionalString (bookmark.tags != [ ])
|
|
" TAGS=\"${escapeXML (concatStringsSep "," bookmark.tags)}\""
|
|
}>${escapeXML bookmark.name}</A>'';
|
|
|
|
directoryToHTML = indentLevel: directory: ''
|
|
${indent indentLevel}<DT>${
|
|
if directory.toolbar then
|
|
''
|
|
<H3 ADD_DATE="1" LAST_MODIFIED="1" PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Toolbar''
|
|
else
|
|
''<H3 ADD_DATE="1" LAST_MODIFIED="1">${escapeXML directory.name}''
|
|
}</H3>
|
|
${indent indentLevel}<DL><p>
|
|
${allItemsToHTML (indentLevel + 1) directory.bookmarks}
|
|
${indent indentLevel}</DL><p>'';
|
|
|
|
itemToHTMLOrRecurse = indentLevel: item:
|
|
if item ? "url" then
|
|
bookmarkToHTML indentLevel item
|
|
else
|
|
directoryToHTML indentLevel item;
|
|
|
|
allItemsToHTML = indentLevel: bookmarks:
|
|
lib.concatStringsSep "\n"
|
|
(map (itemToHTMLOrRecurse indentLevel) bookmarks);
|
|
|
|
bookmarkEntries = allItemsToHTML 1 bookmarks;
|
|
in pkgs.writeText "${packageName}-bookmarks.html" ''
|
|
<!DOCTYPE NETSCAPE-Bookmark-file-1>
|
|
<!-- This is an automatically generated file.
|
|
It will be read and overwritten.
|
|
DO NOT EDIT! -->
|
|
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
|
|
<TITLE>Bookmarks</TITLE>
|
|
<H1>Bookmarks Menu</H1>
|
|
<DL><p>
|
|
${bookmarkEntries}
|
|
</DL>
|
|
'';
|
|
|
|
mkNoDuplicateAssertion = entities: entityKind:
|
|
(let
|
|
# Return an attribute set with entity IDs as keys and a list of
|
|
# entity names with corresponding ID as value. An ID is present in
|
|
# the result only if more than one entity has it. The argument
|
|
# entities is a list of AttrSet of one id/name pair.
|
|
findDuplicateIds = entities:
|
|
filterAttrs (_entityId: entityNames: length entityNames != 1)
|
|
(zipAttrs entities);
|
|
|
|
duplicates = findDuplicateIds (mapAttrsToList
|
|
(entityName: entity: { "${toString entity.id}" = entityName; })
|
|
entities);
|
|
|
|
mkMsg = entityId: entityNames:
|
|
" - ID ${entityId} is used by " + concatStringsSep ", " entityNames;
|
|
in {
|
|
assertion = duplicates == { };
|
|
message = ''
|
|
Must not have a ${appName} ${entityKind} with an existing ID but
|
|
'' + concatStringsSep "\n" (mapAttrsToList mkMsg duplicates);
|
|
});
|
|
|
|
wrapPackage = package:
|
|
let
|
|
# The configuration expected by the Firefox wrapper.
|
|
fcfg = { enableGnomeExtensions = cfg.enableGnomeExtensions; };
|
|
|
|
# A bit of hackery to force a config into the wrapper.
|
|
browserName =
|
|
package.browserName or (builtins.parseDrvName package.name).name;
|
|
|
|
# The configuration expected by the Firefox wrapper builder.
|
|
bcfg = setAttrByPath [ browserName ] fcfg;
|
|
in if package == null then
|
|
null
|
|
else if isDarwin then
|
|
package
|
|
else if isWrapped then
|
|
package.override (old: {
|
|
cfg = old.cfg or { } // fcfg;
|
|
extraPolicies = (old.extraPolicies or { }) // cfg.policies;
|
|
})
|
|
else
|
|
(pkgs.wrapFirefox.override { config = bcfg; }) package { };
|
|
in {
|
|
options = setAttrByPath modulePath {
|
|
enable = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
example = true;
|
|
description = ''
|
|
Whether to enable ${appName}.${
|
|
optionalString (description != null) " ${description}"
|
|
}
|
|
${optionalString (!visible)
|
|
"See `${moduleName}` for more configuration options."}
|
|
'';
|
|
};
|
|
|
|
package = mkOption {
|
|
inherit visible;
|
|
type = with types; nullOr package;
|
|
default = pkgs.${defaultPackageName};
|
|
defaultText = literalExpression "pkgs.${packageName}";
|
|
example = literalExpression ''
|
|
pkgs.${packageName}.override {
|
|
# See nixpkgs' firefox/wrapper.nix to check which options you can use
|
|
nativeMessagingHosts = [
|
|
# Gnome shell native connector
|
|
pkgs.gnome-browser-connector
|
|
# Tridactyl native connector
|
|
pkgs.tridactyl-native
|
|
];
|
|
}
|
|
'';
|
|
description = ''
|
|
The ${appName} package to use. If state version ≥ 19.09 then
|
|
this should be a wrapped ${appName} package. For earlier state
|
|
versions it should be an unwrapped ${appName} package.
|
|
Set to `null` to disable installing ${appName}.
|
|
'';
|
|
};
|
|
|
|
languagePacks = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ ];
|
|
description = ''
|
|
The language packs to install. Available language codes can be found
|
|
on the releases page:
|
|
`https://releases.mozilla.org/pub/firefox/releases/''${version}/linux-x86_64/xpi/`,
|
|
replacing `''${version}` with the version of ${appName} you have.
|
|
'';
|
|
example = [ "en-GB" "de" ];
|
|
};
|
|
|
|
name = mkOption {
|
|
internal = true;
|
|
type = types.str;
|
|
default = name;
|
|
example = "Firefox";
|
|
description = "The name of the browser.";
|
|
};
|
|
|
|
wrappedPackageName = mkOption {
|
|
internal = true;
|
|
type = with types; nullOr str;
|
|
default = wrappedPackageName;
|
|
description = "Name of the wrapped browser package.";
|
|
};
|
|
|
|
vendorPath = mkOption {
|
|
internal = true;
|
|
type = with types; nullOr str;
|
|
default = with platforms;
|
|
if isDarwin then
|
|
darwin.vendorPath or null
|
|
else
|
|
linux.vendorPath or null;
|
|
example = ".mozilla";
|
|
description =
|
|
"Directory containing the native messaging hosts directory.";
|
|
};
|
|
|
|
configPath = mkOption {
|
|
internal = true;
|
|
type = types.str;
|
|
default = with platforms;
|
|
if isDarwin then darwin.configPath else linux.configPath;
|
|
example = ".mozilla/firefox";
|
|
description = "Directory containing the ${appName} configuration files.";
|
|
};
|
|
|
|
nativeMessagingHosts = optionalAttrs (cfg.vendorPath != null) (mkOption {
|
|
inherit visible;
|
|
type = types.listOf types.package;
|
|
default = [ ];
|
|
description = ''
|
|
Additional packages containing native messaging hosts that should be
|
|
made available to ${appName} extensions.
|
|
'';
|
|
});
|
|
|
|
finalPackage = mkOption {
|
|
inherit visible;
|
|
type = with types; nullOr package;
|
|
readOnly = true;
|
|
description = "Resulting ${appName} package.";
|
|
};
|
|
|
|
policies = optionalAttrs (wrappedPackageName != null) (mkOption {
|
|
inherit visible;
|
|
type = types.attrsOf jsonFormat.type;
|
|
default = { };
|
|
description =
|
|
"[See list of policies](https://mozilla.github.io/policy-templates/).";
|
|
example = {
|
|
DefaultDownloadDirectory = "\${home}/Downloads";
|
|
BlockAboutConfig = true;
|
|
};
|
|
});
|
|
|
|
profileVersion = mkOption {
|
|
internal = true;
|
|
type = types.nullOr types.ints.unsigned;
|
|
default = if isDarwin then null else 2;
|
|
description = "profile version, set null for nix-darwin";
|
|
};
|
|
|
|
profiles = mkOption {
|
|
inherit visible;
|
|
type = types.attrsOf (types.submodule ({ config, name, ... }: {
|
|
options = {
|
|
name = mkOption {
|
|
type = types.str;
|
|
default = name;
|
|
description = "Profile name.";
|
|
};
|
|
|
|
id = mkOption {
|
|
type = types.ints.unsigned;
|
|
default = 0;
|
|
description = ''
|
|
Profile ID. This should be set to a unique number per profile.
|
|
'';
|
|
};
|
|
|
|
preConfig = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
description = ''
|
|
Extra preferences to add to {file}`user.js`, before
|
|
[](#opt-programs.firefox.profiles._name_.settings).
|
|
|
|
Use [](#opt-programs.firefox.profiles._name_.extraConfig), unless
|
|
you want to overwrite in
|
|
[](#opt-programs.firefox.profiles._name_.settings), then use this
|
|
option.
|
|
'';
|
|
};
|
|
|
|
settings = mkOption {
|
|
type = types.attrsOf (jsonFormat.type // {
|
|
description =
|
|
"${appName} preference (int, bool, string, and also attrs, list, float as a JSON string)";
|
|
});
|
|
default = { };
|
|
example = literalExpression ''
|
|
{
|
|
"browser.startup.homepage" = "https://nixos.org";
|
|
"browser.search.region" = "GB";
|
|
"browser.search.isUS" = false;
|
|
"distribution.searchplugins.defaultLocale" = "en-GB";
|
|
"general.useragent.locale" = "en-GB";
|
|
"browser.bookmarks.showMobileBookmarks" = true;
|
|
"browser.newtabpage.pinned" = [{
|
|
title = "NixOS";
|
|
url = "https://nixos.org";
|
|
}];
|
|
}
|
|
'';
|
|
description = ''
|
|
Attribute set of ${appName} preferences.
|
|
|
|
${appName} only supports int, bool, and string types for
|
|
preferences, but home-manager will automatically
|
|
convert all other JSON-compatible values into strings.
|
|
'';
|
|
};
|
|
|
|
extraConfig = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
description = ''
|
|
Extra preferences to add to {file}`user.js`.
|
|
'';
|
|
};
|
|
|
|
userChrome = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
description = "Custom ${appName} user chrome CSS.";
|
|
example = ''
|
|
/* Hide tab bar in FF Quantum */
|
|
@-moz-document url(chrome://browser/content/browser.xul), url(chrome://browser/content/browser.xhtml) {
|
|
#TabsToolbar {
|
|
visibility: collapse !important;
|
|
margin-bottom: 21px !important;
|
|
}
|
|
|
|
#sidebar-box[sidebarcommand="treestyletab_piro_sakura_ne_jp-sidebar-action"] #sidebar-header {
|
|
visibility: collapse !important;
|
|
}
|
|
}
|
|
'';
|
|
};
|
|
|
|
userContent = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
description = "Custom ${appName} user content CSS.";
|
|
example = ''
|
|
/* Hide scrollbar in FF Quantum */
|
|
*{scrollbar-width:none !important}
|
|
'';
|
|
};
|
|
|
|
bookmarks = mkOption {
|
|
internal = !enableBookmarks;
|
|
type = let
|
|
bookmarkSubmodule = types.submodule ({ config, name, ... }: {
|
|
options = {
|
|
name = mkOption {
|
|
type = types.str;
|
|
default = name;
|
|
description = "Bookmark name.";
|
|
};
|
|
|
|
tags = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ ];
|
|
description = "Bookmark tags.";
|
|
};
|
|
|
|
keyword = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = "Bookmark search keyword.";
|
|
};
|
|
|
|
url = mkOption {
|
|
type = types.str;
|
|
description = "Bookmark url, use %s for search terms.";
|
|
};
|
|
};
|
|
}) // {
|
|
description = "bookmark submodule";
|
|
};
|
|
|
|
bookmarkType = types.addCheck bookmarkSubmodule (x: x ? "url");
|
|
|
|
directoryType = types.submodule ({ config, name, ... }: {
|
|
options = {
|
|
name = mkOption {
|
|
type = types.str;
|
|
default = name;
|
|
description = "Directory name.";
|
|
};
|
|
|
|
bookmarks = mkOption {
|
|
type = types.listOf nodeType;
|
|
default = [ ];
|
|
description = "Bookmarks within directory.";
|
|
};
|
|
|
|
toolbar = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
Make this the toolbar directory. Note, this does _not_
|
|
mean that this directory will be added to the toolbar,
|
|
this directory _is_ the toolbar.
|
|
'';
|
|
};
|
|
};
|
|
}) // {
|
|
description = "directory submodule";
|
|
};
|
|
|
|
nodeType = types.either bookmarkType directoryType;
|
|
in with types;
|
|
coercedTo (attrsOf nodeType) attrValues (listOf nodeType);
|
|
default = [ ];
|
|
example = literalExpression ''
|
|
[
|
|
{
|
|
name = "wikipedia";
|
|
tags = [ "wiki" ];
|
|
keyword = "wiki";
|
|
url = "https://en.wikipedia.org/wiki/Special:Search?search=%s&go=Go";
|
|
}
|
|
{
|
|
name = "kernel.org";
|
|
url = "https://www.kernel.org";
|
|
}
|
|
{
|
|
name = "Nix sites";
|
|
toolbar = true;
|
|
bookmarks = [
|
|
{
|
|
name = "homepage";
|
|
url = "https://nixos.org/";
|
|
}
|
|
{
|
|
name = "wiki";
|
|
tags = [ "wiki" "nix" ];
|
|
url = "https://wiki.nixos.org/";
|
|
}
|
|
];
|
|
}
|
|
]
|
|
'';
|
|
description = ''
|
|
Preloaded bookmarks. Note, this may silently overwrite any
|
|
previously existing bookmarks!
|
|
'';
|
|
};
|
|
|
|
path = mkOption {
|
|
type = types.str;
|
|
default = name;
|
|
description = "Profile path.";
|
|
};
|
|
|
|
isDefault = mkOption {
|
|
type = types.bool;
|
|
default = config.id == 0;
|
|
defaultText = "true if profile ID is 0";
|
|
description = "Whether this is a default profile.";
|
|
};
|
|
|
|
search = mkOption {
|
|
type = types.submodule (args:
|
|
import ./profiles/search.nix {
|
|
inherit (args) config;
|
|
inherit lib pkgs appName;
|
|
package = cfg.finalPackage;
|
|
modulePath = modulePath ++ [ "profiles" name "search" ];
|
|
profilePath = config.path;
|
|
});
|
|
default = { };
|
|
description = "Declarative search engine configuration.";
|
|
};
|
|
|
|
containersForce = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
Whether to force replace the existing containers configuration.
|
|
This is recommended since ${appName} will replace the symlink on
|
|
every launch, but note that you'll lose any existing configuration
|
|
by enabling this.
|
|
'';
|
|
};
|
|
|
|
containers = mkOption {
|
|
type = types.attrsOf (types.submodule ({ name, ... }: {
|
|
options = {
|
|
name = mkOption {
|
|
type = types.str;
|
|
default = name;
|
|
description = "Container name, e.g., shopping.";
|
|
};
|
|
|
|
id = mkOption {
|
|
type = types.ints.unsigned;
|
|
default = 0;
|
|
description = ''
|
|
Container ID. This should be set to a unique number per container in this profile.
|
|
'';
|
|
};
|
|
|
|
# List of colors at
|
|
# https://searchfox.org/mozilla-central/rev/5ad226c7379b0564c76dc3b54b44985356f94c5a/toolkit/components/extensions/parent/ext-contextualIdentities.js#32
|
|
color = mkOption {
|
|
type = types.enum [
|
|
"blue"
|
|
"turquoise"
|
|
"green"
|
|
"yellow"
|
|
"orange"
|
|
"red"
|
|
"pink"
|
|
"purple"
|
|
"toolbar"
|
|
];
|
|
default = "pink";
|
|
description = "Container color.";
|
|
};
|
|
|
|
icon = mkOption {
|
|
type = types.enum [
|
|
"briefcase"
|
|
"cart"
|
|
"circle"
|
|
"dollar"
|
|
"fence"
|
|
"fingerprint"
|
|
"gift"
|
|
"vacation"
|
|
"food"
|
|
"fruit"
|
|
"pet"
|
|
"tree"
|
|
"chill"
|
|
];
|
|
default = "fruit";
|
|
description = "Container icon.";
|
|
};
|
|
};
|
|
}));
|
|
default = { };
|
|
example = {
|
|
"shopping" = {
|
|
id = 1;
|
|
color = "blue";
|
|
icon = "cart";
|
|
};
|
|
"dangerous" = {
|
|
id = 2;
|
|
color = "red";
|
|
icon = "fruit";
|
|
};
|
|
};
|
|
description = ''
|
|
Attribute set of container configurations. See
|
|
[Multi-Account
|
|
Containers](https://support.mozilla.org/en-US/kb/containers)
|
|
for more information.
|
|
'';
|
|
};
|
|
extensions = mkOption {
|
|
type = types.coercedTo (types.listOf types.package) (packages: {
|
|
packages = mkIf (builtins.length packages > 0) (warn ''
|
|
In order to support declarative extension configuration,
|
|
extension installation has been moved from
|
|
${moduleName}.profiles.<profile>.extensions
|
|
to
|
|
${moduleName}.profiles.<profile>.extensions.packages
|
|
'' packages);
|
|
}) (types.submodule {
|
|
options = {
|
|
packages = mkOption {
|
|
type = types.listOf types.package;
|
|
default = [ ];
|
|
example = literalExpression ''
|
|
with pkgs.nur.repos.rycee.firefox-addons; [
|
|
privacy-badger
|
|
]
|
|
'';
|
|
description = ''
|
|
List of ${name} add-on packages to install for this profile.
|
|
Some pre-packaged add-ons are accessible from the Nix User Repository.
|
|
Once you have NUR installed run
|
|
|
|
```console
|
|
$ nix-env -f '<nixpkgs>' -qaP -A nur.repos.rycee.firefox-addons
|
|
```
|
|
|
|
to list the available ${name} add-ons.
|
|
|
|
Note that it is necessary to manually enable these extensions
|
|
inside ${name} after the first installation.
|
|
|
|
To automatically enable extensions add
|
|
`"extensions.autoDisableScopes" = 0;`
|
|
to
|
|
[{option}`${moduleName}.profiles.<profile>.settings`](#opt-${moduleName}.profiles._name_.settings)
|
|
'';
|
|
};
|
|
|
|
settings = mkOption {
|
|
default = { };
|
|
example = literalExpression ''
|
|
{
|
|
# Example with uBlock origin's extensionID
|
|
"uBlock0@raymondhill.net".settings = {
|
|
selectedFilterLists = [
|
|
"ublock-filters"
|
|
"ublock-badware"
|
|
"ublock-privacy"
|
|
"ublock-unbreak"
|
|
"ublock-quick-fixes"
|
|
];
|
|
};
|
|
|
|
# Example with Stylus' UUID-form extensionID
|
|
"{7a7a4a92-a2a0-41d1-9fd7-1e92480d612d}".settings = {
|
|
dbInChromeStorage = true; # required for Stylus
|
|
}
|
|
}
|
|
'';
|
|
description = ''
|
|
Attribute set of options for each extension.
|
|
The keys of the attribute set consist of the ID of the extension
|
|
or its UUID wrapped in curly braces.
|
|
'';
|
|
type = types.attrsOf (types.submodule {
|
|
options = {
|
|
settings = mkOption {
|
|
type = types.attrsOf jsonFormat.type;
|
|
description =
|
|
"Json formatted options for the specified extensionID";
|
|
};
|
|
force = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
example = true;
|
|
description = ''
|
|
Forcibly override any existing configuration for
|
|
this extension.
|
|
'';
|
|
};
|
|
};
|
|
});
|
|
};
|
|
};
|
|
});
|
|
default = { };
|
|
description = ''
|
|
Submodule for installing and configuring extensions.
|
|
'';
|
|
example = literalExpression ''
|
|
{
|
|
packages = with pkgs.nur.repos.rycee.firefox-addons; [
|
|
ublock-origin
|
|
];
|
|
settings."uBlock0@raymondhill.net".settings = {
|
|
selectedFilterLists = [
|
|
"ublock-filters"
|
|
"ublock-badware"
|
|
"ublock-privacy"
|
|
"ublock-unbreak"
|
|
"ublock-quick-fixes"
|
|
];
|
|
};
|
|
}
|
|
'';
|
|
};
|
|
};
|
|
}));
|
|
default = { };
|
|
description = "Attribute set of ${appName} profiles.";
|
|
};
|
|
|
|
enableGnomeExtensions = mkOption {
|
|
inherit visible;
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
Whether to enable the GNOME Shell native host connector. Note, you
|
|
also need to set the NixOS option
|
|
`services.gnome.gnome-browser-connector.enable` to
|
|
`true`.
|
|
'';
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable ({
|
|
assertions = [
|
|
(hm.assertions.assertPlatform moduleName pkgs supportedPlatforms)
|
|
|
|
(let
|
|
defaults =
|
|
catAttrs "name" (filter (a: a.isDefault) (attrValues cfg.profiles));
|
|
in {
|
|
assertion = cfg.profiles == { } || length defaults == 1;
|
|
message = "Must have exactly one default ${appName} profile but found "
|
|
+ toString (length defaults) + optionalString (length defaults > 1)
|
|
(", namely " + concatStringsSep ", " defaults);
|
|
})
|
|
|
|
(let
|
|
getContainers = profiles:
|
|
flatten
|
|
(mapAttrsToList (_: value: (attrValues value.containers)) profiles);
|
|
|
|
findInvalidContainerIds = profiles:
|
|
filter (container: container.id >= 4294967294)
|
|
(getContainers profiles);
|
|
in {
|
|
assertion = cfg.profiles == { }
|
|
|| length (findInvalidContainerIds cfg.profiles) == 0;
|
|
message = "Container id must be smaller than 4294967294 (2^32 - 2)";
|
|
})
|
|
|
|
{
|
|
assertion = cfg.languagePacks == [ ] || cfg.package != null;
|
|
message = ''
|
|
'${moduleName}.languagePacks' requires '${moduleName}.package'
|
|
to be set to a non-null value.
|
|
'';
|
|
}
|
|
|
|
(mkNoDuplicateAssertion cfg.profiles "profile")
|
|
] ++ (mapAttrsToList
|
|
(_: profile: mkNoDuplicateAssertion profile.containers "container")
|
|
cfg.profiles);
|
|
|
|
warnings = optional (cfg.enableGnomeExtensions or false) ''
|
|
Using '${moduleName}.enableGnomeExtensions' has been deprecated and
|
|
will be removed in the future. Please change to overriding the package
|
|
configuration using '${moduleName}.package' instead. You can refer to
|
|
its example for how to do this.
|
|
'';
|
|
|
|
home.packages = lib.optional (cfg.finalPackage != null) cfg.finalPackage;
|
|
|
|
home.file = mkMerge ([{
|
|
"${cfg.configPath}/profiles.ini" =
|
|
mkIf (cfg.profiles != { }) { text = profilesIni; };
|
|
}] ++ optional (cfg.vendorPath != null) {
|
|
"${nativeMessagingHostsPath}" = {
|
|
source =
|
|
"${nativeMessagingHostsJoined}/lib/mozilla/native-messaging-hosts";
|
|
recursive = true;
|
|
};
|
|
} ++ flip mapAttrsToList cfg.profiles (_: profile:
|
|
# Merge the regular profile settings with extension settings
|
|
mkMerge ([{
|
|
"${profilesPath}/${profile.path}/.keep".text = "";
|
|
|
|
"${profilesPath}/${profile.path}/chrome/userChrome.css" =
|
|
mkIf (profile.userChrome != "") { text = profile.userChrome; };
|
|
|
|
"${profilesPath}/${profile.path}/chrome/userContent.css" =
|
|
mkIf (profile.userContent != "") { text = profile.userContent; };
|
|
|
|
"${profilesPath}/${profile.path}/user.js" = mkIf (profile.preConfig
|
|
!= "" || profile.settings != { } || profile.extraConfig != ""
|
|
|| profile.bookmarks != [ ]) {
|
|
text =
|
|
mkUserJs profile.preConfig profile.settings profile.extraConfig
|
|
profile.bookmarks profile.extensions.settings;
|
|
};
|
|
|
|
"${profilesPath}/${profile.path}/containers.json" =
|
|
mkIf (profile.containers != { }) {
|
|
text = mkContainersJson profile.containers;
|
|
force = profile.containersForce;
|
|
};
|
|
|
|
"${profilesPath}/${profile.path}/search.json.mozlz4" =
|
|
mkIf (profile.search.enable) {
|
|
enable = profile.search.enable;
|
|
force = profile.search.force;
|
|
source = profile.search.file;
|
|
};
|
|
|
|
"${profilesPath}/${profile.path}/extensions" =
|
|
mkIf (profile.extensions.packages != [ ]) {
|
|
source = let
|
|
extensionsEnvPkg = pkgs.buildEnv {
|
|
name = "hm-firefox-extensions";
|
|
paths = profile.extensions.packages;
|
|
};
|
|
in "${extensionsEnvPkg}/share/mozilla/${extensionPath}";
|
|
recursive = true;
|
|
force = true;
|
|
};
|
|
}] ++
|
|
# Add extension settings as separate attributes
|
|
optional (profile.extensions.settings != { }) (mkMerge (mapAttrsToList
|
|
(name: settingConfig: {
|
|
"${profilesPath}/${profile.path}/browser-extension-data/${name}/storage.js" =
|
|
{
|
|
force = settingConfig.force;
|
|
text = generators.toJSON { } settingConfig.settings;
|
|
};
|
|
}) profile.extensions.settings)))));
|
|
} // setAttrByPath modulePath {
|
|
finalPackage = wrapPackage cfg.package;
|
|
|
|
policies = {
|
|
ExtensionSettings = lib.mkIf (cfg.languagePacks != [ ]) (listToAttrs (map
|
|
(lang:
|
|
nameValuePair "langpack-${lang}@firefox.mozilla.org" {
|
|
installation_mode = "normal_installed";
|
|
install_url =
|
|
"https://releases.mozilla.org/pub/firefox/releases/${cfg.package.version}/linux-x86_64/xpi/${lang}.xpi";
|
|
}) cfg.languagePacks));
|
|
};
|
|
});
|
|
}
|