2026-02-22 19:57:42 +00:00
|
|
|
{
|
|
|
|
|
pkgs,
|
|
|
|
|
lib,
|
2026-02-23 22:08:18 +00:00
|
|
|
sLib,
|
2026-02-22 19:57:42 +00:00
|
|
|
info,
|
|
|
|
|
...
|
|
|
|
|
}:
|
|
|
|
|
|
|
|
|
|
let
|
|
|
|
|
submodule =
|
|
|
|
|
{ name, ... }:
|
|
|
|
|
{
|
|
|
|
|
options = {
|
|
|
|
|
packages = lib.mkOption {
|
|
|
|
|
type =
|
2026-02-23 22:08:18 +00:00
|
|
|
sLib.compose
|
|
|
|
|
[
|
|
|
|
|
lib.types.listOf
|
|
|
|
|
(lib.types.either lib.types.package)
|
|
|
|
|
(lib.types.submodule)
|
|
|
|
|
]
|
|
|
|
|
{
|
|
|
|
|
options = {
|
|
|
|
|
name = lib.mkOption {
|
|
|
|
|
type = lib.types.str;
|
|
|
|
|
default = name;
|
|
|
|
|
example = "ublock-origin";
|
|
|
|
|
description = "The short name of the extension. To find the short name look at the extension's download link.";
|
|
|
|
|
};
|
2026-02-22 19:57:42 +00:00
|
|
|
|
2026-02-23 22:08:18 +00:00
|
|
|
guid = lib.mkOption {
|
|
|
|
|
type = lib.types.str;
|
|
|
|
|
example = "uBlock0@raymondhill.net";
|
|
|
|
|
description = "The guid of the extension. To find the guid go to https://addons.mozilla.org/api/v5/addons/addon/\${shortName}/";
|
|
|
|
|
};
|
2026-02-22 19:57:42 +00:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
default = [ ];
|
|
|
|
|
description = ''
|
|
|
|
|
Extensions to install for this profile. Takes either a package or a description of an extension.
|
|
|
|
|
* If a package is given, the extension will be preinstalled.
|
|
|
|
|
* If a description is given then ${info.name} will download the extension on startup.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
settings = lib.mkOption {
|
|
|
|
|
type =
|
|
|
|
|
let
|
|
|
|
|
jsonFormat = pkgs.formats.json { };
|
|
|
|
|
in
|
|
|
|
|
lib.types.attrsOf jsonFormat.type;
|
|
|
|
|
default = { };
|
|
|
|
|
description = "Settings for this extension, accessible by the managed storage API. The extension must be referenced by id.";
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
# (Package -> a) -> ({ name, guid } -> a) -> Extension -> a
|
|
|
|
|
withExtension =
|
|
|
|
|
f: g: x:
|
|
|
|
|
if lib.isDerivation x then f x else g x;
|
|
|
|
|
|
|
|
|
|
# ublock-origin wants its settings as a string, which is annoying
|
|
|
|
|
# to merge. For convenience, you can keep ublock-origin's config
|
|
|
|
|
# as a set and this function will turn it into a string at the
|
|
|
|
|
# last minute. As a bonus, we can also do other magic for other
|
|
|
|
|
# extensions if need be.
|
|
|
|
|
magicInfo = {
|
|
|
|
|
"uBlock0@raymondhill.net" =
|
|
|
|
|
x:
|
|
|
|
|
x
|
|
|
|
|
// lib.optionalAttrs (x ? adminSettings) {
|
2026-02-23 22:08:18 +00:00
|
|
|
adminSettings = sLib.compose [
|
2026-02-22 19:57:42 +00:00
|
|
|
builtins.toJSON
|
2026-02-23 22:08:18 +00:00
|
|
|
(
|
2026-02-22 19:57:42 +00:00
|
|
|
y:
|
|
|
|
|
y
|
|
|
|
|
// lib.optionalAttrs (y ? userFilters) {
|
|
|
|
|
userFilters = builtins.concatStringsSep "\n" y.userFilters;
|
|
|
|
|
}
|
|
|
|
|
)
|
2026-02-23 22:08:18 +00:00
|
|
|
(
|
2026-02-22 19:57:42 +00:00
|
|
|
y:
|
|
|
|
|
y
|
|
|
|
|
// lib.optionalAttrs (y ? urlFilteringString) {
|
|
|
|
|
urlFilteringString = builtins.concatStringsSep "\n" y.urlFilteringString;
|
|
|
|
|
}
|
|
|
|
|
)
|
2026-02-23 22:08:18 +00:00
|
|
|
(
|
2026-02-22 19:57:42 +00:00
|
|
|
y:
|
|
|
|
|
y
|
|
|
|
|
// lib.optionalAttrs (y ? hostnameSwitchesString) {
|
|
|
|
|
hostnameSwitchesString = builtins.concatStringsSep "\n" y.hostnameSwitchesString;
|
|
|
|
|
}
|
|
|
|
|
)
|
2026-02-23 22:08:18 +00:00
|
|
|
(
|
2026-02-22 19:57:42 +00:00
|
|
|
y:
|
|
|
|
|
y
|
|
|
|
|
// lib.optionalAttrs (y ? dynamicFilteringString) {
|
|
|
|
|
dynamicFilteringString = builtins.concatStringsSep "\n" y.dynamicFilteringString;
|
|
|
|
|
}
|
|
|
|
|
)
|
2026-02-23 22:08:18 +00:00
|
|
|
] x.adminSettings;
|
2026-02-22 19:57:42 +00:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
magic = guid: magicInfo.${guid} or lib.trivial.id;
|
|
|
|
|
in
|
|
|
|
|
{
|
|
|
|
|
options = {
|
|
|
|
|
extensions = lib.mkOption {
|
|
|
|
|
type = lib.types.submodule submodule;
|
|
|
|
|
default = { };
|
|
|
|
|
# example = {
|
|
|
|
|
# packages = [
|
|
|
|
|
# nur.repos.rycee.firefox-addons.ublock-origin
|
|
|
|
|
# { name = "decentraleyes"; guid = "jid1-BoFifL9Vbdl2zQ@jetpack"; }
|
|
|
|
|
# ];
|
|
|
|
|
# settings."uBlock0@raymondhill.net" = {
|
|
|
|
|
# whitelist = [ "example.org" ];
|
|
|
|
|
# };
|
|
|
|
|
# };
|
|
|
|
|
description = "The extensions to install.";
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
process =
|
|
|
|
|
{ extensions, ... }:
|
|
|
|
|
{
|
|
|
|
|
policies = {
|
|
|
|
|
"3rdparty".Extensions = extensions.settings;
|
2026-02-23 22:08:18 +00:00
|
|
|
ExtensionSettings = sLib.compose [
|
2026-02-22 19:57:42 +00:00
|
|
|
builtins.listToAttrs
|
2026-02-23 22:08:18 +00:00
|
|
|
(builtins.map (
|
2026-02-22 19:57:42 +00:00
|
|
|
withExtension
|
|
|
|
|
(x: {
|
|
|
|
|
name = x.addonId;
|
|
|
|
|
value = {
|
|
|
|
|
installation_mode = "force_installed";
|
|
|
|
|
private_browsing = true;
|
|
|
|
|
install_url = "file://${x}/share/mozilla/extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}/${x.addonId}.xpi";
|
|
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
(x: {
|
|
|
|
|
name = x.guid;
|
|
|
|
|
value = {
|
|
|
|
|
installation_mode = "force_installed";
|
|
|
|
|
private_browsing = true;
|
|
|
|
|
install_url = "https://addons.mozilla.org/firefox/downloads/latest/${x.name}/latest.xpi";
|
|
|
|
|
};
|
|
|
|
|
})
|
2026-02-23 22:08:18 +00:00
|
|
|
))
|
|
|
|
|
] extensions.packages;
|
2026-02-22 19:57:42 +00:00
|
|
|
};
|
|
|
|
|
prefs."extensions.autoDisableScopes" = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
postprocess =
|
|
|
|
|
{ policies, ... }:
|
|
|
|
|
lib.optionalAttrs (policies ? "3rdparty".Extensions) {
|
2026-02-23 22:08:18 +00:00
|
|
|
policies."3rdparty".Extensions = builtins.mapAttrs magic (policies."3rdparty".Extensions or { });
|
2026-02-22 19:57:42 +00:00
|
|
|
};
|
|
|
|
|
}
|