# Firefox has deprecated "side-loading" of extensions From [the announcement](https://blog.mozilla.org/addons/2020/03/10/support-for-extension-sideloading-has-ended/): > Today marks the release of Firefox 74 and as we announced last fall, > developers will no longer be able to install extensions without > the user taking an action. This installation method was typically > done through application installers, and is commonly referred to as > “sideloading.” > > If you are the developer of an extension that installs itself via > sideloading, please make sure that your users can install the > extension from your own website or from addons.mozilla.org (AMO). Of course, it is still possible to side-load. The browser itself is installing the extension, and the browser isn't even closed source. It can't hide what it's doing, just obfuscate and write annoying blog posts about how taking away features is making users safer. I spent some time yesterday figuring out how to side-load extensions and writing code to do this for my own purposes. I got a script working that gives an automated extension install into a new profile based on copying out of some other profile where the extension had been installed by firefox interactively. What I'm doing is very coarse -- mostly just copying entire files, but editing the paths within them to refer to the new extension directory instead of the old one. This works for me since I want to copy my entire list of extensions, but you want to surgically install just one extension. I didn't go that far with my code but I think I can tell you how to start the surgery based on what I found. You just need to edit some JSON. (BTW [jq](https://github.com/stedolan/jq) is great.) To get an extension running in a firefox profile, you need to edit the following files in the profile dir: ``` ./extension-settings.json ./extension-preferences.json ./extensions.json ./addonStartup.json.lz4 ./prefs.js ``` * `/extensions/` Put your `*.xpi` here. * `extensions.json` Each extension needs a long entry in the `addons` array * `extension-settings.json` Used to give extensions some special privileges, refers to extension by id (the `id` field in `extensions.json`) * `extension-preferences.json` Used to give extensions privilege to run in private tab, refers to extensions by id * `addonStartup.json.lz4` Contains one `addons` object within each of the four labels "app-builtin", "app-profile", "app-system-defaults", "app-system-share". I use https://github.com/jusw85/mozlz4 to decompress and compress this file. * prefs.js Needed to set `extensions.webextensions.uuids` or to make an extension run as the homepage or new tab page without user interaction. I also use it to bypass confirmation/warning popups, mozilla upgrade notice, etc. Here is an example of the JSON for each file for one extension. This is generated by script and the values are edited (by jq) so that only one extension is shown. ## addonStartup.json.lz4 ``` { "app-profile": { "addons": { "dependencies": [], "enabled": true, "lastModifiedTime": 1592563110000, "loader": null, "path": "extension@tabliss.io.xpi", "rootURI": "jar:file:///home/d/.mozilla/firefox/fm0hmz3f.second_gen4/extensions/extension@tabliss.io.xpi!/", "runInSafeMode": false, "signedState": 2, "telemetryKey": "extension%40tabliss.io:2.0.3", "version": "2.0.3" } } } ``` ``` + mozlz4 addonStartup.json.lz4 + jq -c '.["app-profile"]["addons"]["extension@tabliss.io"] | keys' ["dependencies","enabled","lastModifiedTime","loader","path","rootURI","runInSafeMode","signedState","telemetryKey","version"] ``` ## extension-preferences.json ``` { "extension@tabliss.io": { "permissions": [ "internal:privateBrowsingAllowed" ], "origins": [] } } ``` ``` + jq -c '.["extension@tabliss.io"] | keys' extension-preferences.json ["origins","permissions"] ``` ## extensions.json ``` { "addons": [ { "id": "extension@tabliss.io", "syncGUID": "{c95f9e0d-6450-4383-87e4-2244586de7f5}", "version": "2.0.3", "type": "extension", "loader": null, "updateURL": null, "optionsURL": null, "optionsType": null, "optionsBrowserStyle": true, "aboutURL": null, "defaultLocale": { "name": "Tabliss", "description": "A beautiful New Tab page with many customisable backgrounds and widgets that does not require any permissions.", "creator": null, "developers": null, "translators": null, "contributors": null }, "visible": true, "active": true, "userDisabled": false, "appDisabled": false, "embedderDisabled": false, "installDate": 1592563110000, "updateDate": 1592563110000, "applyBackgroundUpdates": 1, "path": "/home/d/.mozilla/firefox/fm0hmz3f.second_gen4/extensions/extension@tabliss.io.xpi", "skinnable": false, "sourceURI": "https://addons.mozilla.org/firefox/downloads/file/3549950/tabliss_new_tab-2.0.3-fx.xpi?src=search", "releaseNotesURI": null, "softDisabled": false, "foreignInstall": false, "strictCompatibility": true, "locales": [], "targetApplications": [ { "id": "toolkit@mozilla.org", "minVersion": "54.0", "maxVersion": "*" } ], "targetPlatforms": [], "signedState": 2, "seen": true, "dependencies": [], "incognito": "spanning", "userPermissions": { "permissions": [ "storage" ], "origins": [] }, "optionalPermissions": { "permissions": [], "origins": [] }, "icons": { "32": "icons/32.png", "48": "icons/48.png", "96": "icons/96.png", "128": "icons/128.png" }, "iconURL": null, "blocklistState": 0, "blocklistURL": null, "startupData": null, "hidden": false, "installTelemetryInfo": { "source": "amo", "sourceURL": "https://addons.mozilla.org/en-US/firefox/addon/tabliss/?src=search", "method": "amWebAPI" }, "recommendationState": { "validNotAfter": 1744746924000, "validNotBefore": 1586958924000, "states": [ "recommended" ] }, "rootURI": "jar:file:///home/d/.mozilla/firefox/fm0hmz3f.second_gen4/extensions/extension@tabliss.io.xpi!/", "location": "app-profile" } ] } ``` ``` + jq -c '.addons[] | select(.id == "extension@tabliss.io") | keys' extensions.json ["aboutURL","active","appDisabled","applyBackgroundUpdates","blocklistState","blocklistURL","defaultLocale","dependencies","embedderDisabled","foreignInstall","hidden","iconURL","icons","id","incognito","installDate","installTelemetryInfo","loader","locales","location","optionalPermissions","optionsBrowserStyle","optionsType","optionsURL","path","recommendationState","releaseNotesURI","rootURI","seen","signedState","skinnable","softDisabled","sourceURI","startupData","strictCompatibility","syncGUID","targetApplications","targetPlatforms","type","updateDate","updateURL","userDisabled","userPermissions","version","visible"] ``` ## extension-settings.json ``` { "url_overrides": { "newTabURL": { "precedenceList": { "id": "extension@tabliss.io", "installDate": 1592563110000, "value": "moz-extension://5457f46b-efee-42b2-bea9-19bcb10f7618/index.html", "enabled": true } } } } ``` ``` + jq -c keys extension-settings.json ["commands","default_search","homepageNotification","newTabNotification","prefs","tabHideNotification","url_overrides","version"] ``` ## prefs.js ``` user_pref("extensions.webextensions.uuids", "\{\"extension@tabliss.io\":\"5457f46b-efee-42b2-bea9-19bcb10f7618\"\}"); ``` In total, I copy these prefs (...`.mstone` is assigned value "ignore" rather than copied): ``` browser.newtab.extensionControlled browser.newtab.privateAllowed browser.reader.detectedFirstArticle browser.rights.3.shown browser.startup.homepage browser.startup.homepage_override.buildID browser.startup.homepage_override.mstone browser.toolbarbuttons.introduced.pocket-button datareporting.policy.dataSubmissionPolicyAcceptedVersion datareporting.policy.dataSubmissionPolicyNotifiedTime extensions.webextensions.uuids ``` It appears only the last value -- `extensions.webextensions.uuids` -- is crucial. I know it's needed if you want the `browser.startup.homepage` to refer to such a uuid (i.e., to use an extension as the homepage). If you don't include it, mozilla will regenerate the uuids and any copied reference to the old uuid (hiding anywhere) will fail. This only matters if you are trying to copy configuration that refers to that UUID, otherwise Firefox just generates a new UUID for them. But since special preferences used by (at least) uBlock0 and tabliss.io extensions require UUID in `extension-settings.json`, you might need the configuration even if you don't want actual configuration.