Checking Firefox Preferences Programmatically

Forum rules
Before you post please read how to get help
Post Reply
User avatar
Termy
Level 7
Level 7
Posts: 1586
Joined: Mon Sep 04, 2017 8:49 pm
Location: UK
Contact:

Checking Firefox Preferences Programmatically

Post by Termy »

In this thread (page 1), a user shared some interesting Firefox configuration parameters which I thought would make excellent candidates for UbuChk. However, I'm running into some issues. I should point out that this is regarding Perl and not Shell. However, you don't need to know Perl to be able to help me here, and I'm a Shell nerd so I can always translate your Shell suggestions.

I'm as far as understanding that there are two files the user has to create if they want to have their own preferences set either globally or user-side, without using Firefox itself to set them (via about:config). This isn't much use with UbuChk, because I don't want to set the values (UbuChk isn't about doing but informing), I want to check for them being set by the user, and assuming the user has 'user.js', for example, isn't great, because most probably won't.

I've looked at <user_profile>/prefs.js but that, at least for me, has only around 500 configurations, whereas Firefox has in total over 4000. Is 'prefs.js' just what the user has defined to anything other than the default? If so, that's still great. I just need to work around the issue of UbuChk checking these values when Firefox is still running; may just have to check for whether Firefox is running, then inform the user accordingly.

I've accounted for the different profiles the user can have, by reading the 'profiles.ini' file and looking at the first 'Default' key, then deferring to the profile listed there. That'll work for now until I add a key the user can change in UbuChk's configuration file to set a specific profile they want to check, assuming this goes well.

Has anyone reading this post ever managed to successfully programmatically check and/or set Firefox configuration parameters for the current user? If so, can you describe as much as you're willing how you did it?

I'm using Firefox 84.0.2 and don't have in place (yet) any sort of Firefox version check, as I'm not even sure how this feature would fail in different versions of Firefox. :?

To put this in BASH context, to give you a general idea what I'm wanting:

Code: Select all

#!/usr/bin/env bash

# To run, use, for example: PROFILE='cool.default' ./script.sh
# Assuming you saved this script as 'script.sh' and run it from
# the current directory with it having executable rights.

while read; do
    [[ $REPLY == //* ]] && continue

    StripLeft=${REPLY#user_pref(}
    StripRight=${StripLeft%);}

    Key=${StripRight%,*}
    Key=${Key%%,*}

    if [ -n "$Key" ]; then
        Value=${StripRight#\"*\",}

        declare -A Config["${Key//\"/}"]=${Value# }
    fi
done < "$HOME"/.mozilla/firefox/$PROFILE/prefs.js

for Key in "${!Config[@]}"; {
    printf '%s\n' "$Key"
}
Instead of just printing the keys in that for loop, imagine I'm checking for the value of the key being as desired. Can I rely on this approach?
I use Linux Mint 18.3 with Cinnamon in a VirtualBox VM for testing & sandboxing.

I'm LearnLinux (LL) on YouTube: https://www.youtube.com/c/learnlinux
I'm also terminalforlife (TFL) on GitHub: https://github.com/terminalforlife
1000
Level 4
Level 4
Posts: 371
Joined: Wed Jul 29, 2020 2:14 am

Re: Checking Firefox Preferences Programmatically

Post by 1000 »

You can add

Code: Select all

#----------------{
NC='\e[0m'    # Reset Color
YW='\e[0;33m' # Yellow ECHO
GN='\e[0;32m' # Green ECHO
RD='\e[0;31m' # Red ECHO

MESSAGE_DEBUG() {
    #DEBUG="OFF / ON"
    DEBUG="ON"
        if [[ "$DEBUG" == "ON" ]] ; then
            echo -e "${YW}  DEBUG: $1 ${NC}"
        fi
}

MESSAGE_INFO() {
    echo "----"
    echo -e "${GN}  INFO: $1 ${NC}"
    echo "----"
}

MESSAGE_ERROR() {
    # print text in red and redirect to error
	echo -e "${RD}  ERROR: $1 ${NC}" 1>&2
}
#----------------}


DETECTED_PROFILES=$(ls -t "$HOME"/.mozilla/firefox/  | grep default)
NUMBER_OF_PROFILES=$(wc -l <<< "$DETECTED_PROFILES")
PROFILE=$(head -n1 <<< "$DETECTED_PROFILES")
    if [ "$NUMBER_OF_PROFILES" -gt "1" ] ; then
        MESSAGE_DEBUG "We found number of profiles = $NUMBER_OF_PROFILES"
        MESSAGE_DEBUG "Newer profile is $PROFILE"
    elif [ "$NUMBER_OF_PROFILES" -lt "1" ] ; then
        MESSAGE_ERROR "We can not find firefox profile." 
        exit 1
    fi
Explanation:

I have 2 profiles

Code: Select all

$ ls -t -l "$HOME"/.mozilla/firefox/  | grep default 
drwx------ 14 user user 4096 jan 29 09:49 zdzzkqzz.default-release
drwx------  9 user user 4096 nov 16 15:10 1f6wzpz0.default
default-release file is newer from 2021 year.
default file is from 2020 year
https://support.mozilla.org/bm/questions/1264072
It may look different for each person, so I decided to use the latest profile.

I have 220 options from this file

Code: Select all

$ bash script1  | wc -l
220
About firefox files https://support.mozilla.org/en-US/kb/pr ... -user-data
1000
Level 4
Level 4
Posts: 371
Joined: Wed Jul 29, 2020 2:14 am

Re: Checking Firefox Preferences Programmatically

Post by 1000 »

I've looked at <user_profile>/prefs.js but that, at least for me, has only around 500 configurations, whereas Firefox has in total over 4000. Is 'prefs.js' just what the user has defined to anything other than the default?
You are absolutely right.

http://kb.mozillazine.org/Prefs.js_file
mozillazine wrote:It only stores changes made to the defaults, after they are written back to disk.
Edit
I add something else .
This or other script seems like a better idea than trusting a web browser. So this is good idea.
You don't need to make sure if Firefox is running unless you are going to edit the file.
But keep in mind that Firefox is a little erratic and it will be hard to detect if Firefox creates a new directory with settings.

Edit
Every user can create own profile.
https://support.mozilla.org/en-US/kb/pr ... x-profiles
User avatar
Termy
Level 7
Level 7
Posts: 1586
Joined: Mon Sep 04, 2017 8:49 pm
Location: UK
Contact:

Re: Checking Firefox Preferences Programmatically

Post by Termy »

Thank you, 1000. I'll check all that out. :) I'll continue then with this feature in UbuChk by using the prefs.js file.
I use Linux Mint 18.3 with Cinnamon in a VirtualBox VM for testing & sandboxing.

I'm LearnLinux (LL) on YouTube: https://www.youtube.com/c/learnlinux
I'm also terminalforlife (TFL) on GitHub: https://github.com/terminalforlife
User avatar
Termy
Level 7
Level 7
Posts: 1586
Joined: Mon Sep 04, 2017 8:49 pm
Location: UK
Contact:

Re: Checking Firefox Preferences Programmatically

Post by Termy »

So this was going well enough, but something occurred to me which has put a real spanner in the works. I can't let the user know (via verbose output) to what something is current set; this isn't ideal at all, but even if I looked past that, how do I check to see what the current value is? I get the impression that this is just too unreliable.

I can't rely on 'prefs.js' because it only shows the values currently assigned as something other than the defaults, so how can I alert the user of a value being set as something UbuChk can't even see? Even if I grabbed a list of every single default, how do I know that default list isn't being overridden elsewhere? Hence the unreliability of this functionality. There has to be a better way.

I've got an array (hash) storing all of the keys and associated values, which I'm checking. Here's the (Perl) code with which I'm currently working:

Code: Select all

            if (length($DefaultProfile) > 0) { 
                my $PrefsJS = "$FirefoxDir/$DefaultProfile/prefs.js";

                if (-f $PrefsJS and -r $PrefsJS) {
                    open(my $FH, '<', $PrefsJS);

                    my %Keys;
                    while (<$FH>) {
                        /^user_pref\(/ or next;
                        chomp();

                        my $BetweenParens = $_ =~ s/^user_pref\((.*)\);$/$1/r;
                        
                        # Naive, but should work for most keys, for now.
                        my ($Key, $Value) = split(',', $BetweenParens);

                        $Keys{$Key =~ tr/"//dr} = $Value =~ s/^\s+//r
                    }

                    close($FH);

                    foreach my $Key (sort({$a cmp $b} keys(%Keys))) {

                        if ($Key eq 'geo.enabled') {
                            $Keys{$Key} eq 'false' or next;

                            Alert(
                                3462,
                                "",
                                ""
                            )
                        }
                    }
                } else {
                    warn('Unable to read Firefox preferences')
                }
            } else {
                warn('Unable to determine default Firefox profile')
            }
        } else {
            warn('Unable to find or access Firefox profile data')
        }
I checked the repositories and there's sadly no Firefox module for Perl, which would've been a great alternative. :( Was hoping there'd be a way to communicate directly with Firefox to simply know to which value it considers -- at that moment -- each key is set. Does it have some special API other than JavaScript relying on already being within Firefox?
I use Linux Mint 18.3 with Cinnamon in a VirtualBox VM for testing & sandboxing.

I'm LearnLinux (LL) on YouTube: https://www.youtube.com/c/learnlinux
I'm also terminalforlife (TFL) on GitHub: https://github.com/terminalforlife
1000
Level 4
Level 4
Posts: 371
Joined: Wed Jul 29, 2020 2:14 am

Re: Checking Firefox Preferences Programmatically

Post by 1000 »

You don't need to check the current value.
You can inform the user that an option is missing in prefs.js file.
Then user can check the browser settings manually, because he already knows what option he must search.

Maybe other way will be plugin for Firefox, but you need ask Firefox developer
is it possible to read the settings from the plugin / addon.
Last edited by 1000 on Fri Jan 29, 2021 1:43 pm, edited 2 times in total.
User avatar
Termy
Level 7
Level 7
Posts: 1586
Joined: Mon Sep 04, 2017 8:49 pm
Location: UK
Contact:

Re: Checking Firefox Preferences Programmatically

Post by Termy »

1000 wrote:
Fri Jan 29, 2021 1:29 pm
You don't need to check the current value.
You can inform the user that an option is missing in prefs.js file.
I'll give that approach a go.

UbuChk generally shows the current values for things, for SystemD, APT, SSH, etc, so I was trying to keep to that standard, so that the user can consistently see what's going on and what their environment currently is when they use the verbose flag. I guess I'll just show the user that a key for which UbuChk checks isn't found in 'prefs.js'.
1000 wrote:
Fri Jan 29, 2021 1:29 pm
is it possible to read the settings from the plugin / addon.
I have no idea. Are you thinking of using a Firefox addon which UbuChk could instead read? It wouldn't work well with UbuChk, because the point of UbuChk is not to do anything but just recommend, suggest, and advise. It could be done during installation of UbuChk, if it's possible to set up an addon outside of Firefox, but that's quite invasive. A user might not want any of that functionality at all -- I know I wouldn't.

Thank you for all of your thoughts and suggestions, BTW. :)
I use Linux Mint 18.3 with Cinnamon in a VirtualBox VM for testing & sandboxing.

I'm LearnLinux (LL) on YouTube: https://www.youtube.com/c/learnlinux
I'm also terminalforlife (TFL) on GitHub: https://github.com/terminalforlife
1000
Level 4
Level 4
Posts: 371
Joined: Wed Jul 29, 2020 2:14 am

Re: Checking Firefox Preferences Programmatically

Post by 1000 »

Edit
I am thinking of a new plugin for Firefox.

If you use for example uBlock or other plugin
, you can ask also this developer is it possible to add other options.
For example uBlock can block WebRTC (option inside settings) , which reveals local IP address.
by default, the option is unchecked because online messaging uses this feature.
Maybe he can rebuild own plugin...

With WebRTC is like a bit with java in a browser.
Or you turn it off and you can only read and you are safe.
Or you turn on java support and you can do more, but you're not safe .
User avatar
Welcome
Level 6
Level 6
Posts: 1028
Joined: Wed Aug 19, 2020 11:38 am

Re: Checking Firefox Preferences Programmatically

Post by Welcome »

Termy wrote:
Thu Jan 28, 2021 10:21 pm
...
Has anyone reading this post ever managed to successfully programmatically check and/or set Firefox configuration parameters for the current user? If so, can you describe as much as you're willing how you did it?
I don't know if this will help, but I offer my experience with Firefox prefs. For Firefox versions before 52, I would extract all of the prefs using a small bit of JavaScript in the JavaScript Scratchpad. Since Mozilla Firefox changes the prefs every release, I used this extraction to compare the changes between the versions. I did the comparison manually using a program like Meld, but others used slightly more automated methods (you might be able to find a copy of Pref_Diff_Tool_V2.html). I did this so that I could maintain my custom prefs.js (or user.js) and thereby enforce my preferences.

Luckily, I searched my historical files and found the old JavaScript file. I've attached it (Prefs_Exporter_V6-1.js) below in case you find it useful, or informative at the least.

I stopped using this method, since I believe Mozilla Firefox effectively stopped this capability. I now use arkenfox/user.js to get the latest changes each release (but I still use Meld to check the changes). You might want to look at arkenfox/user.js and their scripts https://github.com/arkenfox/user.js. NOTE: I don't use their scripts so I can't comment on them.
Termy wrote:
Thu Jan 28, 2021 10:21 pm
...
Instead of just printing the keys in that for loop, imagine I'm checking for the value of the key being as desired. Can I rely on this approach?
As mentioned above, Firefox changes prefs every release, so you'll need to keep track of the changes.

Prefs_Exporter_V6-1.js

Code: Select all

// Must ensure devtools settings are set: devtools.chrome.enabled;true

var sdk = false;
if (!('Cc' in this)) {
    try {
        // add-on SDK version
        this.Cc = require('chrome') .Cc;
        this.Ci = require('chrome') .Ci;
        this.log = console.error.bind(console);
        this.sdk = true;
        log('using SDK');
    }
    catch (ex) {
        // Scratchpad on about:newtab version
        this.Cc = Components['classes'];
        this.log = console.log.bind(console);
        log('using scratchpad');
    }
}


(function() {
  let verInfoStr = "Pref Exporter 0.6"
  try
  {
    // Output format: either "TEXT" or "JSON"
    const format = "TEXT";

    // Default basename (to which .txt or .json will be appended)
    const defaultBasename = "ExportedPrefs";

    // Use an application-specific prefix with default filename?
    const appSpecFilenamePrefix = true;

    // TEXT output only: Export a status field (locked, userset, default)?
    const exportStatusField = false;

    // TEXT output only: Export a type field (boolean, integer, string)?
    const exportTypeField = false;

    // TEXT output only: Separator between pref fields
    const sep = ";";

    // TEXT output only: End of line separator
    const eol  = "\r\n";

    // Nothing below here should need adjusting unless you are improving the code
    let prefBranch = Cc["@mozilla.org/preferences-service;1"]
                          .getService(Ci.nsIPrefService).getBranch('');
    let filename;
    if(appSpecFilenamePrefix)
    {
      let app;
      let appVersion;
      if(prefBranch.getPrefType("torbrowser.version") == prefBranch.PREF_STRING)
      {
        app = "TorBrowser";
        appVersion = prefBranch.getCharPref("torbrowser.version");
      }
      else
      {
        let appInfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
        app = appInfo.name.replace(" ", "_");
        appVersion = appInfo.version;
      }
      filename = app + "_" + appVersion + "_" + defaultBasename;
    }
    else filename = defaultBasename;

    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    fp.init(window, "Pref Exporter output file", Ci.nsIFilePicker.modeSave);
    if(format == "JSON")
    {
      fp.defaultString = filename + ".json";
      fp.appendFilter("JSON", "*.json");
    }
    else
    {
      fp.defaultString = filename + ".txt";
      fp.appendFilters(Ci.nsIFilePicker.filterText);
    }
    fp.appendFilters(Ci.nsIFilePicker.filterAll);
    let rv = fp.show();
    if ((rv == Ci.nsIFilePicker.returnOK) || (rv == Ci.nsIFilePicker.returnReplace))
    {
      let invalidPrefTypes = 0;
      let unknownPrefTypes = 0;
      let prefList = [];
      let count = {value:0};
      let children = prefBranch.getChildList("",count);
      for(let i=0; i<count.value; i++)
      {
        let pref = {
                     name:   "",
                     status: "",
                     type:   "",
                     value:  "",
                   };
        pref.name = children[i];
        if(prefBranch.prefIsLocked(pref.name))
          pref.status = "locked";
        else if(prefBranch.prefHasUserValue(pref.name))
          pref.status = "userset";
        else pref.status = "default";
        switch(prefBranch.getPrefType(pref.name))
        {
          case prefBranch.PREF_BOOL:
            pref.type = "boolean";
            pref.value = prefBranch.getBoolPref(pref.name);
            break;
          case prefBranch.PREF_INT:
            pref.type = "integer";
            pref.value = prefBranch.getIntPref(pref.name);
            break;
          case prefBranch.PREF_STRING:
            pref.type = "string";
            pref.value = prefBranch.getComplexValue(pref.name, Ci.nsISupportsString).data;
            if((pref.status == "default") &&
               (/^chrome:\/\/.+\/locale\/.+\.properties/.test(pref.value)))
            {
              try
              {
                pref.value = prefBranch.getComplexValue(pref.name, Ci.nsIPrefLocalizedString).data;
              }
              catch(e)
              {
                // NOP
              }
            }
            break;
          case prefBranch.PREF_INVALID:
            pref.type = "invalid";
            pref.value = "PrefTypeInvalid_ValueNotAvailable";
            invalidPrefsTypes++;
            break;
          default:
            pref.type = "unknown";
            pref.value = "PrefTypeUnknown_ValueNotAvailable";
            unknownPrefTypes++;
            break;
        }
        prefList.push(pref);
      }
      prefList.sort(function(a, b)
                    {
                      if(a.name < b.name)
                        return -1;
                      if(a.name > b.name)
                        return 1;
                      return 0;
                    });
      let output = "";
      if(format == "JSON")
      {
        let gcsdif = {};
        gcsdif.dif = "GCSDIF";
        gcsdif.version = "0.1";
        gcsdif.prefList = prefList;
        output += JSON.stringify(gcsdif);
      }
      else
      {
        for(let i=0; i<prefList.length; i++)
        {
          output += prefList[i].name;
          if(exportStatusField)
            output += sep + prefList[i].status;
          if(exportTypeField)
            output += sep + prefList[i].type;
          output += sep + prefList[i].value.toString() + eol;
        }
      }
      let foStream = Cc["@mozilla.org/network/file-output-stream;1"]
                            .createInstance(Ci.nsIFileOutputStream);
      foStream.init(fp.file, 0x02 | 0x08 | 0x20, parseInt("0666", 8), 0);
      let converter = Cc["@mozilla.org/intl/converter-output-stream;1"]
                            .createInstance(Ci.nsIConverterOutputStream);
      converter.init(foStream, "UTF-8", 0, 0);
      converter.writeString(output);
      converter.close();
      let resultMsg = "Exported " + prefList.length + " prefs.\n";
      if(invalidPrefTypes)
        resultMsg += "\nInvalid pref types: " + invalidPrefTypes;
      if(unknownPrefTypes)
        resultMsg += "\nUnknown pref types: " + unknownPrefTypes;
      resultMsg += "\nYour export may contain sensitive information.  " +
                   "Please protect the file appropriately.\n"
      let promptSvc = Cc["@mozilla.org/embedcomp/prompt-service;1"]
                            .getService(Ci.nsIPromptService);
      promptSvc.alert(null, verInfoStr, resultMsg);
    }
  }
  catch(e)
  {
      let promptSvc = Cc["@mozilla.org/embedcomp/prompt-service;1"]
                            .getService(Ci.nsIPromptService);
      promptSvc.alert(null, verInfoStr, e);
  }
})();

Here's some interesting numbers (I think). The approximate number of prefs in a few of the releases (based on my extractions):

27.0.1 - 5968
45.7.0 - 2752
51.0.1 - 3458
52.0 - 2884

Sometimes the script didn't capture all of the prefs....
1000
Level 4
Level 4
Posts: 371
Joined: Wed Jul 29, 2020 2:14 am

Re: Checking Firefox Preferences Programmatically

Post by 1000 »

Tip about Firefox (I don't know if there is or is not that option)
viewtopic.php?f=47&t=342573
Post Reply

Return to “Scripts & Bash”