Skip to content

Instantly share code, notes, and snippets.

@dstensnes
Last active February 26, 2024 04:18
Show Gist options
  • Save dstensnes/1e87ed55dfbe5cca329e9cd5eb579064 to your computer and use it in GitHub Desktop.
Save dstensnes/1e87ed55dfbe5cca329e9cd5eb579064 to your computer and use it in GitHub Desktop.
Firefox profile wrapper script
#! /bin/sh
##### DESCRIPTION #####
# This is a small wrapper script for running firefox with multiple profiles.
# By default it just wraps the normal firefox executable functionality, but
# appends a path to a profile named "default".
#
# You can create additional profiles like this:
# firefox normal netflix 'https://www.netflix.com/'
#
# It also has support for creating profiles somewhat similar to
# google chrome's app-mode functionality (which works
# great for netflix by the way!). All it does is hide
# the URL-bar and tabs, but keyboard shortcuts still works
# (like Ctrl+T for new tab, and Ctrl+Tab to cycle tabs,
# Ctrl+W to close a tab). Create an app profile like this:
# firefox app netflix 'https://www.netflix.com/'
#
# Everything else is passed along to the real firefox
# executable like normal, so options etc should work
#
# Notes:
# * I preseed a lot of settings in new profiles to prevent UI tours
# and data sharing popups. I have however tried to disable all kind
# of datasharing and telemetry, but further updates to firefox may
# require additional settings to turn them off in the future.
# * To delete a profile, delete the folder inside "$HOME/$PROFILEFOLDERDIR"
# and the corresponding wrapper script created inside "$SCRIPTPATH"
# * Clicking the Alt button once allows access to preferences and addons
# from the old application menu, in case you need to install additional
# addons or do other tweaks.
#
# Licence: Public domain without restrictions.
#
# Warnings:
# * As usual, use at your own risk. It works for me, it should hopefully
# work for you if you follow the instructions above. I accept no
# responsibility for bugs here.
#
# Enjoy!
#
# - Daniel Stensnes
#
#
#
##### CONFIGURATION #####
#
# Path to Firefox executable
FIREFOXEXECUTABLE="$HOME/.local/apps/firefox/firefox"
# Folder to hold all firefox profiles. Always relative to home directory! No absolute urls! No trailing slash!
PROFILEFOLDERDIR=".firefox-profiles"
# Space separated URLs to extensions to install in all profiles
EXTENSIONURLS='https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/'
# Path where a wrapper script for starting this profile will be created
SCRIPTPATH="$HOME/.local/bin"
#
#
##### SCRIPT CODE #####
#
syntax () {
echo "$0 \"normal\" <appname> <url>"
echo "$0 \"app\" <appname> <url>"
echo "$0 <url>"
}
# Validate that "$1" looks somewhat like a valid URL
checkIsUrl () {
if [ -z "$(echo "$1" | grep "://")" ] ; then
echo "No URL given. Follow the syntax!"
syntax
exit 1
fi
}
# Preseed some settings in new profiles, to prevent
# UI tours and other popups on every new profile.
mkUserPref () {
echo '
pref("app.shield.optoutstudies.enabled", true);
pref("browser.bookmarks.restore_default_bookmarks", false);
pref("browser.contentblocking.introCount", 20);
pref("browser.newtabpage.activity-stream.feeds.telemetry", false);
pref("browser.newtabpage.activity-stream.telemetry", false);
pref("browser.newtabpage.activity-stream.telemetry.ping.endpoint", "");
pref("browser.newtabpage.enabled", false);
pref("browser.newtabpage.enhanced", false);
pref("browser.newtabpage.storageVersion", 1);
pref("browser.onboarding.enabled", false);
pref("browser.onboarding.notification.finished", true);
pref("browser.onboarding.state", "watermark");
pref("browser.onboarding.notification.finished", true);
pref("browser.onboarding.tour-type", "none");
pref("browser.onboarding.tour.onboarding-tour-addons.completed", true);
pref("browser.onboarding.tour.onboarding-tour-customize.completed", true);
pref("browser.onboarding.tour.onboarding-tour-default-browser.completed", true);
pref("browser.onboarding.tour.onboarding-tour-performance.completed", true);
pref("browser.onboarding.tour.onboarding-tour-private-browsing.completed", true);
pref("browser.onboarding.tour.onboarding-tour-screenshots.completed", true);
pref("browser.link.open_newwindow.restriction", 0);
pref("browser.ping-centre.telemetry", false);
pref("browser.shell.checkDefaultBrowser", false);
pref("browser.shell.didSkipDefaultBrowserCheckOnFirstRun", true);
pref("browser.startup.homepage_override.mstone", "ignore");
pref("browser.uitour.enabled", false);
pref("datareporting.healthreport.uploadEnabled", false);
pref("extensions.pocket.enabled", false);
pref("extensions.shield-recipe-client.enabled", false);
pref("extensions.shield-recipe-client.first_run", false);
pref("extensions.shield-recipe-client.startupExperimentPrefs.browser.newtabpage.activity-stream.enabled", false);
pref("extensions.ui.dictionary.hidden", true);
pref("extensions.ui.experiment.hidden", true);
pref("extensions.ui.locale.hidden", true);
pref("network.cookie.cookieBehavior", 3);
pref("network.dns.disablePrefetch", true);
pref("privacy.donottrackheader.enabled", true);
pref("privacy.clearOnShutdown.cookies", false);
pref("privacy.clearOnShutdown.downloads", false);
pref("privacy.clearOnShutdown.formdata", false);
pref("privacy.clearOnShutdown.history", false);
pref("privacy.clearOnShutdown.sessions", false);
pref("privacy.donottrackheader.enabled", true);
pref("privacy.history.custom", true);
pref("privacy.sanitize.didShutdownSanitize", true);
pref("privacy.sanitize.sanitizeOnShutdown", true);
pref("security.ssl.errorReporting.url", "");
pref("toolkit.telemetry.archive.enabled", false);
pref("toolkit.telemetry.bhrPing.enabled", false);
pref("toolkit.telemetry.enabled", false);
pref("toolkit.telemetry.firstShutdownPing.enabled", false);
pref("toolkit.telemetry.newProfilePing.enabled", false);
pref("toolkit.telemetry.reportingpolicy.firstRun", false);
pref("toolkit.telemetry.server", "");
pref("toolkit.telemetry.shutdownPingSender.enabled", false);
pref("toolkit.telemetry.unified", false);
pref("toolkit.telemetry.updatePing.enabled", false);
pref("browser.uidensity", 1);
pref("lightweightThemes.selectedThemeID", "firefox-compact-dark@mozilla.org");
pref("toolkit.cosmeticAnimations.enabled", false);
pref("datareporting.policy.dataSubmissionPolicyAcceptedVersion", 20);
pref("datareporting.policy.dataSubmissionPolicyNotifiedTime", "1");
' > $1
}
mkProfilePath () {
if [ "$1" != 'app' -a "$1" != 'normal' ] ; then
echo "ERROR: No app-mode given to mkProfilePath"
exit 1
fi
if [ -z "$2" -o -n "$(echo "$2" | grep '://')" ] ; then
echo "ERROR: No profile name given to mkProfilePath"
exit 1
fi
if [ -z "$3" ] ; then
echo "WARNING: No default URL given to mkProfilePath. Not setting a default"
fi
PROFILEPATH="$HOME/$PROFILEFOLDERDIR/$2"
if [ ! -d "$PROFILEPATH" ] ; then
mkdir -p "${PROFILEPATH}"
fi
if [ ! -f "$PROFILEPATH/user.js" ] ; then
mkUserPref "$PROFILEPATH/user.js"
fi
if [ "$1" = "app" ] ; then
mkdir -p "${PROFILEPATH}/chrome"
chmod og-rwx -R "${PROFILEPATH}"
echo $ECHOESCAPECHAROPTION '#TabsToolbar,#nav-bar { visibility: collapse !important; }'>"${PROFILEPATH}/chrome/userChrome.css"
echo $ECHOESCAPECHAROPTION '@-moz-document url-prefix("about:") { * { color: #F9F9FA !important; background-color: #0C0C0C !important; } }'>${PROFILEPATH}/chrome/userContent.css
fi
if [ -n "$SCRIPTPATH" -a ! -f "$HOME/$SCRIPTPATH/$2" -a "$3" != 'no-script' ] ; then
if [ -n "$3" -a "$3" != 'none' ] ; then
echo $ECHOESCAPECHAROPTION '#! /bin/sh\nexport GTK_THEME=Adwaita:light\nexec $HOME/.local/apps/firefox/firefox --class="'$2'" -profile "$HOME/'"$PROFILEFOLDERDIR/$2"'" -no-remote "'"$3"'" &> $HOME/.output.firefox-'"$2"'' > "$SCRIPTPATH/$2"
else
echo $ECHOESCAPECHAROPTION '#! /bin/sh\nexport GTK_THEME=Adwaita:light\nexec $HOME/.local/apps/firefox/firefox --class="'$2'" -profile "$HOME/'"$PROFILEFOLDERDIR/$2"'" -no-remote $* &> $HOME/.output.firefox-'"$2"'' > "$SCRIPTPATH/$2"
fi
chmod 755 "$SCRIPTPATH/$2"
fi
}
installExtensions () {
if [ -n "$EXTENSIONURLS" ] ; then
for EXTURL in $EXTENSIONURLS ; do
$FIREFOXEXECUTABLE -profile "$1" -no-remote "${EXTURL}" &> /dev/null
done
fi
}
if [ -n "$(readlink "/proc/$$/exe" | grep '/bash')" ] ; then
ECHOESCAPECHAROPTION="-e"
else
ECHOESCAPECHAROPTION=""
fi
if [ ! -x "$FIREFOXEXECUTABLE" ] ; then
echo "FIREFOXEXECUTABLE does not point to an executable file (firefox executable)"
exit 1
fi
[ -n "$SCRIPTPATH" -a ! -d "$SCRIPTPATH" ] && mkdir -p "$SCRIPTPATH"
umask 077
case "$1" in
app|normal)
PROFILEPATH="$HOME/$PROFILEFOLDERDIR/$2/"
checkIsUrl "$3"
mkProfilePath "$1" "$2" "$3"
installExtensions "$PROFILEPATH"
;;
help)
syntax
;;
*)
PROFILEPATH="$HOME/$PROFILEFOLDERDIR/default/"
if [ ! -d "$PROFILEPATH" ] ; then
mkProfilePath normal "$PROFILEPATH" no-script
fi
export GTK_THEME=Adwaita:light
exec ${FIREFOXEXECUTABLE} -profile "${PROFILEPATH}" $* &> $HOME/.output.firefox-default
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment