aboutsummaryrefslogtreecommitdiff
path: root/posts
diff options
context:
space:
mode:
Diffstat (limited to 'posts')
-rw-r--r--posts/2023-08-14-emacsify-firefox-init.org180
1 files changed, 180 insertions, 0 deletions
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 <https://www.gnu.org/licenses/>.
+
+# 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
+<https://github.com/mozilla/policy-templates>.
+
+#+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).