1
0
Fork 0
mirror of https://github.com/nix-community/home-manager synced 2024-12-27 04:09:47 +01:00
home-manager/modules/programs/firefox/profiles/search.nix
Kira Bruneau e1aec543f5
thunderbird: support setting search engines (#5697)
* firefox: split search into separate submodule file

* thunderbird: support setting search engines
2024-10-14 13:01:26 +02:00

251 lines
7.9 KiB
Nix

{ config, lib, pkgs, appName, modulePath, profilePath }:
with lib;
let
jsonFormat = pkgs.formats.json { };
# Map of nice field names to internal field names.
# This is intended to be exhaustive and should be
# updated at every version bump.
internalFieldNames = (genAttrs [
"name"
"isAppProvided"
"loadPath"
"hasPreferredIcon"
"updateInterval"
"updateURL"
"iconUpdateURL"
"iconURL"
"iconMapObj"
"metaData"
"orderHint"
"definedAliases"
"urls"
] (name: "_${name}")) // {
searchForm = "__searchForm";
};
processCustomEngineInput = input:
(removeAttrs input [ "icon" ]) // optionalAttrs (input ? icon) {
# Convenience to specify absolute path to icon
iconURL = "file://${input.icon}";
} // (optionalAttrs (input ? iconUpdateURL) {
# Convenience to default iconURL to iconUpdateURL so
# the icon is immediately downloaded from the URL
iconURL = input.iconURL or input.iconUpdateURL;
} // {
# Required for custom engine configurations, loadPaths
# are unique identifiers that are generally formatted
# like: [source]/path/to/engine.xml
loadPath = "[home-manager]/${
concatStringsSep "." (map strings.escapeNixIdentifier
(modulePath ++ [ "engines" input.name ]))
}";
});
processEngineInput = name: input:
let
requiredInput = {
inherit name;
isAppProvided = input.isAppProvided or removeAttrs input [ "metaData" ]
== { };
metaData = input.metaData or { };
};
in if requiredInput.isAppProvided then
requiredInput
else
processCustomEngineInput (input // requiredInput);
buildEngineConfig = name: input:
mapAttrs' (name: value: {
name = internalFieldNames.${name} or name;
inherit value;
}) (processEngineInput name input);
sortEngineConfigs = configs:
let
buildEngineConfigWithOrder = order: name:
let
config = configs.${name} or {
_name = name;
_isAppProvided = true;
_metaData = { };
};
in config // { _metaData = config._metaData // { inherit order; }; };
engineConfigsWithoutOrder = attrValues (removeAttrs configs config.order);
sortedEngineConfigs = (imap buildEngineConfigWithOrder config.order)
++ engineConfigsWithoutOrder;
in sortedEngineConfigs;
engineInput = config.engines // {
# Infer config.default as an app provided
# engine if it's not in config.engines
${config.default} = config.engines.${config.default} or { };
} // {
${config.privateDefault} = config.engines.${config.privateDefault} or { };
};
settings = {
version = 6;
engines = sortEngineConfigs (mapAttrs buildEngineConfig engineInput);
metaData = optionalAttrs (config.default != null) {
current = config.default;
hash = "@hash@";
} // optionalAttrs (config.privateDefault != null) {
private = config.privateDefault;
privateHash = "@privateHash@";
} // {
useSavedOrder = config.order != [ ];
};
};
# Home Manager doesn't circumvent user consent and isn't acting
# maliciously. We're modifying the search outside of the browser, 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 = "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 "
+ "to accordingly.";
salt = if config.default != null then
profilePath + config.default + disclaimer
else
null;
privateSalt = if config.privateDefault != null then
profilePath + config.privateDefault + disclaimer
else
null;
file = pkgs.runCommand "search.json.mozlz4" {
nativeBuildInputs = with pkgs; [ mozlz4a openssl ];
json = builtins.toJSON settings;
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)
mozlz4a <(substituteStream json search.json.in --subst-var hash --subst-var privateHash) "$out"
else
mozlz4a <(echo "$json") "$out"
fi
'';
in {
imports = [ (pkgs.path + "/nixos/modules/misc/meta.nix") ];
meta.maintainers = with maintainers; [ kira-bruneau ];
options = {
enable = mkOption {
type = with types; bool;
default = config.default != null || config.privateDefault != null
|| config.order != [ ] || config.engines != { };
internal = true;
};
force = mkOption {
type = with types; bool;
default = false;
description = ''
Whether to force replace the existing search
configuration. This is recommended since ${appName} will
replace the symlink for the search configuration on every
launch, but note that you'll lose any existing configuration
by enabling this.
'';
};
default = mkOption {
type = with types; nullOr str;
default = null;
example = "DuckDuckGo";
description = ''
The default search engine used in the address bar and search
bar.
'';
};
privateDefault = mkOption {
type = with types; nullOr str;
default = null;
example = "DuckDuckGo";
description = ''
The default search engine used in the Private Browsing.
'';
};
order = mkOption {
type = with types; uniq (listOf str);
default = [ ];
example = [ "DuckDuckGo" "Google" ];
description = ''
The order the search engines are listed in. Any engines that
aren't included in this list will be listed after these in an
unspecified order.
'';
};
engines = mkOption {
type = with types; attrsOf (attrsOf jsonFormat.type);
default = { };
example = literalExpression ''
{
"Nix Packages" = {
urls = [{
template = "https://search.nixos.org/packages";
params = [
{ name = "type"; value = "packages"; }
{ name = "query"; value = "{searchTerms}"; }
];
}];
icon = "''${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
definedAliases = [ "@np" ];
};
"NixOS Wiki" = {
urls = [{ template = "https://wiki.nixos.org/index.php?search={searchTerms}"; }];
iconUpdateURL = "https://wiki.nixos.org/favicon.png";
updateInterval = 24 * 60 * 60 * 1000; # every day
definedAliases = [ "@nw" ];
};
"Bing".metaData.hidden = true;
"Google".metaData.alias = "@g"; # builtin engines only support specifying one additional alias
}
'';
description = ''
Attribute set of search engine configurations. Engines that
only have {var}`metaData` specified will be treated as builtin
to ${appName}.
See [SearchEngine.jsm](https://searchfox.org/mozilla-central/rev/669329e284f8e8e2bb28090617192ca9b4ef3380/toolkit/components/search/SearchEngine.jsm#1138-1177)
in ${appName}'s source for available options. We maintain a
mapping to let you specify all options in the referenced link
without underscores, but it may fall out of date with future
options.
Note, {var}`icon` is also a special option added by Home
Manager to make it convenient to specify absolute icon paths.
'';
};
file = mkOption {
type = with types; path;
default = file;
internal = true;
readOnly = true;
description = ''
Resulting search.json.mozlz4 file.
'';
};
};
}