From c02b46797a5d29994dc560442524012787535027 Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Mon, 14 Aug 2023 13:26:59 +1000 Subject: Add a blogpost emacsify-firefox-init --- posts/2023-08-14-emacsify-firefox-init.org | 180 +++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 posts/2023-08-14-emacsify-firefox-init.org diff --git a/posts/2023-08-14-emacsify-firefox-init.org b/posts/2023-08-14-emacsify-firefox-init.org new file mode 100644 index 0000000..7796356 --- /dev/null +++ b/posts/2023-08-14-emacsify-firefox-init.org @@ -0,0 +1,180 @@ +# Copyright (C) 2023 Yuchen Pei. + +# Permission is granted to copy, distribute and/or modify this +# document under the terms of the GNU Free Documentation License, +# Version 1.3 or any later version published by the Free Software +# Foundation; with no Invariant Sections, no Front-Cover Texts, and +# no Back-Cover Texts. You should have received a copy of the GNU +# Free Documentation License. If not, see . + +# This work is licensed under the Creative Commons +# Attribution-ShareAlike 4.0 International License. To view a copy of +# this license, visit http://creativecommons.org/licenses/by-sa/4.0/ +# or send a letter to Creative Commons, PO Box 1866, Mountain View, CA +# 94042, USA. + +#+title: Emacsify Firefox init + +#+date: <2023-08-14> + +I wanted to deploy Firefox the same way [[https://g.ypei.me/dotted.git/tree/emacs/README.org][I deploy Emacs]], namely +version-controlled plain text config files as the single source of +truth that determines browser settings, extensions and extension +settings on a new session, but during a live session I should still be +able to update my preferences. So for example, when I start firefox it +only loads extensions specified in the config files, but after it is +started I can install new extensions the session, and if I do not add +these extensions to the config files, they should disappear at the +next startup. However user data like history do not belong to configs +and should not be affected. + +Apparently this is too much to ask of Firefox. + +There's [[https://kb.mozillazine.org/User.js_file][=user.js=]], which describes user preferences and should be put +under the user profile directory. It can control things like warnings +in ~about:config~, the startup homepage and more. All settings are in +the form of + +#+begin_src js +user_pref("pref.name", pref_value); +#+end_src + +For example, the following disables DRM and removes it from the UI +settings: + +#+begin_src js +user_pref("media.eme.enabled", false); +user_pref("browser.eme.ui.enabled", false); +#+end_src + +A well-known, well-documented and actively-updated =user.js= with a +privacy focus is by [[https://github.com/arkenfox/user.js][arkenfox]]. + +Then there's also [[https://support.mozilla.org/en-US/kb/customizing-firefox-using-autoconfig][autoconfig]], which locks / disables certain user +preferences, from either the user or extensions. Coming from Emacs, I +find it rather redundant, because the user should be able to +experiment with different preference values in a live session, and +either an extension is trusted and won't change any user preferences +or it is not trusted and should not be installed. + +Another, more powerful tool is [[http://web.archive.org/web/20200929180805/https://developer.mozilla.org/en-US/docs/Mozilla/About_omni.ja_(formerly_omni.jar)][=omni.ja=]], which is an compressed +archive and needs to be inflated and deflated with specific command +flags. It is also updated automatically by firefox updates. Apparently +it can be used to modify "basic" keybindings otherwise impossible to +modify, like C-w (I lost count how many times I pressed C-w to kill +some text while composing in a textbox, only to get the tab killed). I +have not tried it, and I am not sure if it still works given the MDN +page has disappeared. + +Finally, to actually deploy extensions, one has to resort to +[[https://support.mozilla.org/en-US/products/firefox-enterprise/policies-customization-enterprise/policies-overview-enterprise][=policies.json=]]. In GNU/Linux it can only be configured system-wide +and needs to be put in the firefox install directory. It is intended +for sysadmin to use and exert power on poor users, but for our +purpose, we can wear the two hats simultaneously in the hope of +achieving some control. + +As a bonus, on [[https://extensionworkshop.com/documentation/enterprise/enterprise-distribution/][developer/nightly/ESR versions]] with +~xpinstall.signatures.required~ set to true, you can use +=policies.json= to deploy any /unsigned/ extensions. So you don't need +mozilla's approval to write custom extensions and have it permanently +available across sessions. + +There is some overlap in settings covered by =user.js= and +=policies.json=. For example, just to make sure DRM is definitely +disabled and removed from settings system-wide, include the following: + +#+begin_src json +{ + "policies": { + "EncryptedMediaExtensions": { + "Enabled": false, + "Locked": true + } + } +} +#+end_src + +Back to extensions, the way to do it is ExtensionSettings. For +example, the following installs uBlock and [[https://gnu.org/s/librejs][librejs]], as well as removes +google search and firefox dark mode. The ~install_url~ field supports +~http://~ and ~file://~ protocols, the latter does not support home +expansion or relative paths which is a bit of a pain. Also, once an +extension is installed this way, the user cannot remove it during the +session. The full documentation of =policies.json= can be found at +. + +#+begin_src json + "ExtensionSettings": { + "uBlock0@raymondhill.net": { + "installation_mode": "normal_installed", + "install_url": "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi" + }, + "jid1-KtlZuoiikVfFew@jetpack": { + "installation_mode": "normal_installed", + "install_url": "file:///tmp/librejs/librejs.xpi" + }, + "google@search.mozilla.org": { + "installation_mode": "blocked" + }, + "firefox-compact-dark@mozilla.org": { + "installation_mode": "blocked" + } + } +#+end_src + +A problem is that there is no way to allow installing new extensions +just for a session. New extensions installed during one session will +persist across sessions even if it is not specified in +ExtensionSettings. + +The real dead end for this project was reached when I tried deploying +extensions preferences. These preferences include for example +whitelisted js for librejs and userscripts for [[https://www.greasespot.net/][Greasemonkey]]. The +obstacle lies with the way firefox store these preferences, which is +sqlite databases with some [[https://stackoverflow.com/questions/54920939/parsing-fb-puritys-firefox-idb-indexed-database-api-object-data-blob-from-lin/59923297#59923297][nontrivial encoding for both keys and +values]]. Therefore it is generally not possible to deploy with +plaintext configuration. Firefox does provide a [[https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#managed_storage_manifests][managed storage +manifests]] that allows specifying such preferences in =policies.json=. +However, it seems to me most extensions do not use it. The only +extension I use that enables this is [[https://github.com/gorhill/uBlock/wiki/Deploying-uBlock-Origin:-configuration][uBlock]], and here's an example +that disables the uBlock item in the context menu (i.e. the menu that +pops up when you right-click in a webpage) + +#+begin_src json + "3rdparty": { + "Extensions": { + "uBlock0@raymondhill.net": { + "userSettings": [ + [ "contextMenuEnabled", "false" ] + ] + } + } + } +#+end_src + +A workaround would be creating custom versions of extensions, with +user preference built in the xpi files. The appealing part of this +idea is the role change in hacking the source code of extensions. The +default Firefox experience with the extension signing requirement etc. +has made users a rather passive party and left all development +responsibilities to extension devolopers. Checking out source code of +all extensions and customising them make it closer to an Emacs +experience that smoothly transforms users to stakeholders. The +downside of this approach is all the churns required to keep +converting user preferences to patches whenever the former updates, +and to keep patching extensions whenever the upstream projects are +updated. The level of ease of converting user preferences to a patch +for an extension also highly depends on the extension's source code +organisation. It would be much better if extension maintainers could +make this task easier, or even better [[https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/managed][support managed storage +manifest]]. + +As for me, I am going to put a bookmark on this project, and try out +[[https://nyxt.atlas.engineer/][nyxt]]. I have heard it is more customisable with lisp as the +configuration language. There are not as many extensions in the +[[https://www.gnu.org/philosophy/words-to-avoid.en.html#Ecosystem][+ecosystem+]] community, but I suspect most useful extensions can be +easily implemented, and I suspect ublock is not needed if one disables +nonfree javascript. If it turns out nyxt works well, I will port +librejs there too. Having librejs implemented more than once will also +help extract the main logical part of it into a library usable by more +projects (e.g. Emacs). -- cgit v1.2.3