paperwork: fix installing translations (#370379)
[NixPkgs.git] / pkgs / applications / networking / browsers / firefox / wrapper.nix
blob374d581855e1807b6ebe47a912dbedebd506e803
1 { stdenv, lib, makeDesktopItem, makeWrapper, makeBinaryWrapper, lndir, config
2 , buildPackages
3 , jq, xdg-utils, writeText
5 ## various stuff that can be plugged in
6 , ffmpeg, xorg, alsa-lib, libpulseaudio, libcanberra-gtk3, libglvnd, libnotify, opensc
7 , adwaita-icon-theme
8 , browserpass, gnome-browser-connector, uget-integrator, plasma5Packages, bukubrow, pipewire
9 , tridactyl-native
10 , fx-cast-bridge
11 , keepassxc
12 , udev
13 , libkrb5
14 , libva
15 , libgbm
16 , cups
17 , pciutils
18 , vulkan-loader
19 , sndio
20 , libjack2
21 , speechd-minimal
24 ## configurability of the wrapper itself
26 browser:
28 let
29   wrapper =
30     { applicationName ? browser.binaryName or (lib.getName browser)
31     , pname ? applicationName
32     , version ? lib.getVersion browser
33     , desktopName ? # applicationName with first letter capitalized
34       (lib.toUpper (lib.substring 0 1 applicationName) + lib.substring 1 (-1) applicationName)
35     , nameSuffix ? ""
36     , icon ? applicationName
37     , wmClass ? applicationName
38     , nativeMessagingHosts ? []
39     , extraNativeMessagingHosts ? []
40     , pkcs11Modules ? []
41     , useGlvnd ? true
42     , cfg ? config.${applicationName} or {}
44     ## Following options are needed for extra prefs & policies
45     # For more information about anti tracking (german website)
46     # visit https://wiki.kairaven.de/open/app/firefox
47     , extraPrefs ? ""
48     , extraPrefsFiles ? []
49     # For more information about policies visit
50     # https://mozilla.github.io/policy-templates/
51     , extraPolicies ? {}
52     , extraPoliciesFiles ? []
53     , libName ? browser.libName or applicationName # Important for tor package or the like
54     , nixExtensions ? null
55     , hasMozSystemDirPatch ? (lib.hasPrefix "firefox" pname && !lib.hasSuffix "-bin" pname)
56     }:
58     let
59       ffmpegSupport = browser.ffmpegSupport or false;
60       gssSupport = browser.gssSupport or false;
61       alsaSupport = browser.alsaSupport or false;
62       pipewireSupport = browser.pipewireSupport or false;
63       sndioSupport = browser.sndioSupport or false;
64       jackSupport = browser.jackSupport or false;
65       # PCSC-Lite daemon (services.pcscd) also must be enabled for firefox to access smartcards
66       smartcardSupport = cfg.smartcardSupport or false;
68       deprecatedNativeMessagingHost = option: pkg:
69         if (cfg.${option} or false)
70           then
71             lib.warn "The cfg.${option} argument for `firefox.override` is deprecated, please add `pkgs.${pkg.pname}` to `nativeMessagingHosts` instead"
72             [pkg]
73           else [];
75       allNativeMessagingHosts = builtins.map lib.getBin (
76         nativeMessagingHosts
77           ++ deprecatedNativeMessagingHost "enableBrowserpass" browserpass
78           ++ deprecatedNativeMessagingHost "enableBukubrow" bukubrow
79           ++ deprecatedNativeMessagingHost "enableTridactylNative" tridactyl-native
80           ++ deprecatedNativeMessagingHost "enableGnomeExtensions" gnome-browser-connector
81           ++ deprecatedNativeMessagingHost "enableUgetIntegrator" uget-integrator
82           ++ deprecatedNativeMessagingHost "enablePlasmaBrowserIntegration" plasma5Packages.plasma-browser-integration
83           ++ deprecatedNativeMessagingHost "enableFXCastBridge" fx-cast-bridge
84           ++ deprecatedNativeMessagingHost "enableKeePassXC" keepassxc
85           ++ (if extraNativeMessagingHosts != []
86                 then lib.warn "The extraNativeMessagingHosts argument for the Firefox wrapper is deprecated, please use `nativeMessagingHosts`" extraNativeMessagingHosts
87                 else [])
88        );
90        libs = lib.optionals stdenv.hostPlatform.isLinux (
91             [ udev libva libgbm libnotify xorg.libXScrnSaver cups pciutils vulkan-loader ]
92             ++ lib.optional (cfg.speechSynthesisSupport or true) speechd-minimal
93        )
94             ++ lib.optional pipewireSupport pipewire
95             ++ lib.optional ffmpegSupport ffmpeg
96             ++ lib.optional gssSupport libkrb5
97             ++ lib.optional useGlvnd libglvnd
98             ++ lib.optionals (cfg.enableQuakeLive or false)
99             (with xorg; [ stdenv.cc libX11 libXxf86dga libXxf86vm libXext libXt alsa-lib zlib ])
100             ++ lib.optional (config.pulseaudio or true) libpulseaudio
101             ++ lib.optional alsaSupport alsa-lib
102             ++ lib.optional sndioSupport sndio
103             ++ lib.optional jackSupport libjack2
104             ++ lib.optional smartcardSupport opensc
105             ++ pkcs11Modules
106             ++ gtk_modules;
107       gtk_modules = [ libcanberra-gtk3 ];
109       launcherName = "${applicationName}${nameSuffix}";
111       #########################
112       #                       #
113       #   EXTRA PREF CHANGES  #
114       #                       #
115       #########################
116       policiesJson = writeText "policies.json" (builtins.toJSON enterprisePolicies);
118       usesNixExtensions = nixExtensions != null;
120       nameArray = builtins.map(a: a.name) (lib.optionals usesNixExtensions nixExtensions);
122       # Check that every extension has a unqiue .name attribute
123       # and an extid attribute
124       extensions = if nameArray != (lib.unique nameArray) then
125         throw "Firefox addon name needs to be unique"
126       else if browser.requireSigning || !browser.allowAddonSideload then
127         throw "Nix addons are only supported with signature enforcement disabled and addon sideloading enabled (eg. LibreWolf)"
128       else builtins.map (a:
129         if ! (builtins.hasAttr "extid" a) then
130         throw "nixExtensions has an invalid entry. Missing extid attribute. Please use fetchFirefoxAddon"
131         else
132         a
133       ) (lib.optionals usesNixExtensions nixExtensions);
135       enterprisePolicies =
136       {
137         policies = {
138           DisableAppUpdate = true;
139         } //
140         lib.optionalAttrs usesNixExtensions {
141           ExtensionSettings = {
142             "*" = {
143               blocked_install_message = "You can't have manual extension mixed with nix extensions";
144               installation_mode = "blocked";
145             };
146           } // lib.foldr (e: ret:
147             ret // {
148               "${e.extid}" = {
149                 installation_mode = "allowed";
150               };
151             }
152           ) {} extensions;
154           Extensions = {
155             Install = lib.foldr (e: ret:
156               ret ++ [ "${e.outPath}/${e.extid}.xpi" ]
157             ) [] extensions;
158           };
159         } // lib.optionalAttrs smartcardSupport {
160           SecurityDevices = {
161             "OpenSC PKCS#11 Module" = "opensc-pkcs11.so";
162           };
163         }
164         // extraPolicies;
165       };
167       mozillaCfg = ''
168         // First line must be a comment
170         // Disables addon signature checking
171         // to be able to install addons that do not have an extid
172         // Security is maintained because only user whitelisted addons
173         // with a checksum can be installed
174         ${ lib.optionalString usesNixExtensions ''lockPref("xpinstall.signatures.required", false)'' };
175       '';
177       #############################
178       #                           #
179       #   END EXTRA PREF CHANGES  #
180       #                           #
181       #############################
183     in stdenv.mkDerivation (finalAttrs: {
184       __structuredAttrs = true;
185       inherit pname version;
187       desktopItem = makeDesktopItem ({
188         name = launcherName;
189         exec = "${launcherName} --name ${wmClass} %U";
190         inherit icon;
191         inherit desktopName;
192         startupNotify = true;
193         startupWMClass = wmClass;
194         terminal = false;
195       } // (if libName == "thunderbird"
196             then {
197               genericName = "Email Client";
198               comment = "Read and write e-mails or RSS feeds, or manage tasks on calendars.";
199               categories = [
200                 "Network" "Chat" "Email" "Feed" "GTK" "News"
201               ];
202               keywords = [
203                 "mail" "email" "e-mail" "messages" "rss" "calendar"
204                 "address book" "addressbook" "chat"
205               ];
206               mimeTypes = [
207                 "message/rfc822"
208                 "x-scheme-handler/mailto"
209                 "text/calendar"
210                 "text/x-vcard"
211               ];
212               actions = {
213                 profile-manager-window = {
214                   name = "Profile Manager";
215                   exec = "${launcherName} --ProfileManager";
216                 };
217               };
218             }
219             else {
220               genericName = "Web Browser";
221               categories = [ "Network" "WebBrowser" ];
222               mimeTypes = [
223                 "text/html"
224                 "text/xml"
225                 "application/xhtml+xml"
226                 "application/vnd.mozilla.xul+xml"
227                 "x-scheme-handler/http"
228                 "x-scheme-handler/https"
229               ];
230               actions = {
231                 new-window = {
232                   name = "New Window";
233                   exec = "${launcherName} --new-window %U";
234                 };
235                 new-private-window = {
236                   name = "New Private Window";
237                   exec = "${launcherName} --private-window %U";
238                 };
239                 profile-manager-window = {
240                   name = "Profile Manager";
241                   exec = "${launcherName} --ProfileManager";
242                 };
243               };
244             }));
246       nativeBuildInputs = [ makeWrapper lndir jq ];
247       buildInputs = [ browser.gtk3 ];
249       makeWrapperArgs = [
250         "--prefix"
251         "LD_LIBRARY_PATH"
252         ":"
253         "${finalAttrs.libs}"
255         "--suffix"
256         "GTK_PATH"
257         ":"
258         "${lib.concatStringsSep ":" finalAttrs.gtk_modules}"
260         "--suffix" "PATH"
261         ":"
262         "${placeholder "out"}/bin"
264         "--set"
265         "MOZ_APP_LAUNCHER"
266         launcherName
268         "--set"
269         "MOZ_LEGACY_PROFILES"
270         "1"
272         "--set"
273         "MOZ_ALLOW_DOWNGRADE"
274         "1"
276         "--suffix"
277         "XDG_DATA_DIRS"
278         ":"
279         "${adwaita-icon-theme}/share"
281         "--set-default"
282         "MOZ_ENABLE_WAYLAND"
283         "1"
285       ] ++ lib.optionals (!xdg-utils.meta.broken) [
286         # make xdg-open overrideable at runtime
287         "--suffix"
288         "PATH"
289         ":"
290         "${lib.makeBinPath [ xdg-utils ]}"
292       ] ++ lib.optionals hasMozSystemDirPatch [
293         "--set"
294         "MOZ_SYSTEM_DIR"
295         "${placeholder "out"}/lib/mozilla"
297       ] ++ lib.optionals (!hasMozSystemDirPatch && allNativeMessagingHosts != [ ]) [
298         "--run"
299         ''mkdir -p ''${MOZ_HOME:-~/.mozilla}/native-messaging-hosts''
301       ] ++ lib.optionals (!hasMozSystemDirPatch) (lib.concatMap (ext: [
302         "--run"
303         ''ln -sfLt ''${MOZ_HOME:-~/.mozilla}/native-messaging-hosts ${ext}/lib/mozilla/native-messaging-hosts/*''
304       ]) allNativeMessagingHosts);
306       buildCommand = ''
307         if [ ! -x "${browser}/bin/${applicationName}" ]
308         then
309             echo "cannot find executable file \`${browser}/bin/${applicationName}'"
310             exit 1
311         fi
313         #########################
314         #                       #
315         #   EXTRA PREF CHANGES  #
316         #                       #
317         #########################
318         # Link the runtime. The executable itself has to be copied,
319         # because it will resolve paths relative to its true location.
320         # Any symbolic links have to be replicated as well.
321         cd "${browser}"
322         find . -type d -exec mkdir -p "$out"/{} \;
324         find . -type f \( -not -name "${applicationName}" \) -exec ln -sT "${browser}"/{} "$out"/{} \;
326         find . -type f \( -name "${applicationName}" -o -name "${applicationName}-bin" \) -print0 | while read -d $'\0' f; do
327           cp -P --no-preserve=mode,ownership --remove-destination "${browser}/$f" "$out/$f"
328           chmod a+rwx "$out/$f"
329         done
331         # fix links and absolute references
333         find . -type l -print0 | while read -d $'\0' l; do
334           target="$(readlink "$l")"
335           target=''${target/#"${browser}"/"$out"}
336           ln -sfT "$target" "$out/$l"
337         done
339         cd "$out"
341         # create the wrapper
343         executablePrefix="$out/bin"
344         executablePath="$executablePrefix/${applicationName}"
345         oldWrapperArgs=()
347         if [[ -L $executablePath ]]; then
348           # Symbolic link: wrap the link's target.
349           oldExe="$(readlink -v --canonicalize-existing "$executablePath")"
350           rm "$executablePath"
351         elif wrapperCmd=$(${buildPackages.makeBinaryWrapper.extractCmd} "$executablePath"); [[ $wrapperCmd ]]; then
352           # If the executable is a binary wrapper, we need to update its target to
353           # point to $out, but we can't just edit the binary in-place because of length
354           # issues. So we extract the command used to create the wrapper and add the
355           # arguments to our wrapper.
356           parseMakeCWrapperCall() {
357             shift # makeCWrapper
358             oldExe=$1; shift
359             oldWrapperArgs=("$@")
360           }
361           eval "parseMakeCWrapperCall ''${wrapperCmd//"${browser}"/"$out"}"
362           rm "$executablePath"
363         else
364           if read -rn2 shebang < "$executablePath" && [[ $shebang == '#!' ]]; then
365             # Shell wrapper: patch in place to point to $out.
366             sed -i "s@${browser}@$out@g" "$executablePath"
367           fi
368           # Suffix the executable with -old, because -wrapped might already be used by the old wrapper.
369           oldExe="$executablePrefix/.${applicationName}"-old
370           mv "$executablePath" "$oldExe"
371         fi
373         appendToVar makeWrapperArgs --prefix XDG_DATA_DIRS : "$GSETTINGS_SCHEMAS_PATH"
374         concatTo makeWrapperArgs oldWrapperArgs
375         makeWrapper "$oldExe" "''${executablePath}${nameSuffix}" ''${makeWrapperArgs[@]}
376         #############################
377         #                           #
378         #   END EXTRA PREF CHANGES  #
379         #                           #
380         #############################
382         if [ -e "${browser}/share/icons" ]; then
383             mkdir -p "$out/share"
384             ln -s "${browser}/share/icons" "$out/share/icons"
385         else
386             for res in 16 32 48 64 128; do
387             mkdir -p "$out/share/icons/hicolor/''${res}x''${res}/apps"
388             icon=$( find "${browser}/lib/" -name "default''${res}.png" )
389               if [ -e "$icon" ]; then ln -s "$icon" \
390                 "$out/share/icons/hicolor/''${res}x''${res}/apps/${icon}.png"
391               fi
392             done
393         fi
395         install -D -t $out/share/applications $desktopItem/share/applications/*
397       '' + lib.optionalString hasMozSystemDirPatch ''
398         mkdir -p $out/lib/mozilla/native-messaging-hosts
399         for ext in ${toString allNativeMessagingHosts}; do
400             ln -sLt $out/lib/mozilla/native-messaging-hosts $ext/lib/mozilla/native-messaging-hosts/*
401         done
402       '' + ''
404         mkdir -p $out/lib/mozilla/pkcs11-modules
405         for ext in ${toString pkcs11Modules}; do
406             ln -sLt $out/lib/mozilla/pkcs11-modules $ext/lib/mozilla/pkcs11-modules/*
407         done
410         #########################
411         #                       #
412         #   EXTRA PREF CHANGES  #
413         #                       #
414         #########################
415         # user customization
416         mkdir -p $out/lib/${libName}
418         # creating policies.json
419         mkdir -p "$out/lib/${libName}/distribution"
421         POL_PATH="$out/lib/${libName}/distribution/policies.json"
422         rm -f "$POL_PATH"
423         cat ${policiesJson} >> "$POL_PATH"
425         extraPoliciesFiles=(${builtins.toString extraPoliciesFiles})
426         for extraPoliciesFile in "''${extraPoliciesFiles[@]}"; do
427           jq -s '.[0] * .[1]' "$POL_PATH" $extraPoliciesFile > .tmp.json
428           mv .tmp.json "$POL_PATH"
429         done
431         # preparing for autoconfig
432         mkdir -p "$out/lib/${libName}/defaults/pref"
434         echo 'pref("general.config.filename", "mozilla.cfg");' > "$out/lib/${libName}/defaults/pref/autoconfig.js"
435         echo 'pref("general.config.obscure_value", 0);' >> "$out/lib/${libName}/defaults/pref/autoconfig.js"
437         cat > "$out/lib/${libName}/mozilla.cfg" << EOF
438         ${mozillaCfg}
439         EOF
441         extraPrefsFiles=(${builtins.toString extraPrefsFiles})
442         for extraPrefsFile in "''${extraPrefsFiles[@]}"; do
443           cat "$extraPrefsFile" >> "$out/lib/${libName}/mozilla.cfg"
444         done
446         cat >> "$out/lib/${libName}/mozilla.cfg" << EOF
447         ${extraPrefs}
448         EOF
450         mkdir -p $out/lib/${libName}/distribution/extensions
452         #############################
453         #                           #
454         #   END EXTRA PREF CHANGES  #
455         #                           #
456         #############################
457       '';
459       preferLocalBuild = true;
461       libs = lib.makeLibraryPath libs + ":" + lib.makeSearchPathOutput "lib" "lib64" libs;
462       gtk_modules = map (x: x + x.gtkModule) gtk_modules;
464       passthru = { unwrapped = browser; };
466       disallowedRequisites = [ stdenv.cc ];
467       meta = browser.meta // {
468         inherit (browser.meta) description;
469         mainProgram = launcherName;
470         hydraPlatforms = [];
471         priority = (browser.meta.priority or lib.meta.defaultPriority) - 1; # prefer wrapper over the package
472       };
473     });
474 in lib.makeOverridable wrapper