Firefox configuration

2023-03-05 Mozilla Firefox perl Config::Tiny Data::Printer DBI Profile

Firefox is my long time favorite for internet browsing and I have quite an ecosystem built around it. That includes synchronized bookmarks and history, ability to access tabs from other machines (my home laptop, work laptop or my cell phone) and number of addons that make my life a bit easier.

The program uses a directory to store its configuration and other data. On Windows, its is located in %APPDATA%/Mozilla/Firefox, on Unixes it is in ~/.mozilla/firefox. It contains two files, installs.ini and profiles.ini. Former looks like this

[CA9422711AE1A81C]
Default=Profiles/ko4j43ak.dev-edition-default
Locked=1

[A8049CCE42B46439]
Default=Profiles/ab31x35n.dev-edition-default-1
Locked=1

Each entry is encoded installation directory. I tried to find out the encoding, according to some sources on the internet it is the directory hashed with CityHash64 algorithm, but I haven’t found good way to confirm it.

The profiles.ini file lists profile directories like this

[General]
StartWithLastProfile=1
Version=2

[Profile1]
Name=dev-edition-default
IsRelative=1
Path=Profiles/ko4j43ak.dev-edition-default

[Profile0]
Name=default
IsRelative=1
Path=Profiles/ohyktnop.default
Default=1

[InstallCA9422711AE1A81C]
Default=Profiles/ko4j43ak.dev-edition-default
Locked=1

[Profile2]
Name=dev-edition-default-1
IsRelative=1
Path=Profiles/ab31x35n.dev-edition-default-1

[InstallA8049CCE42B46439]
Default=Profiles/ab31x35n.dev-edition-default-1
Locked=1

Probably easiest way to find actual profile directory is to open new tab in Firefox and use about:profiles, where the active one is called default profile

Here is some code to read the profiles config file

use Config::Tiny;
use Data::Printer;

my $ff_dir   = $^O eq 'MSWin32' ? "$ENV{APPDATA}/Mozilla/Firefox" : "$ENV{HOME}/.mozilla/firefox";
my $ini_path = "$ff_dir/profiles.ini";

my $ff_profiles = Config::Tiny->new->read($ini_path);
my @profiles = map { $ff_profiles->{$_} } grep { /^Profile/ } sort keys %$ff_profiles;
p @profiles;

In my case it yields following structure, note the nice output of Data::Printer

[
    [0] {
        Default      1,
        IsRelative   1,
        Name         "default",
        Path         "Profiles/ohyktnop.default"
    },
    [1] {
        IsRelative   1,
        Name         "dev-edition-default",
        Path         "Profiles/ko4j43ak.dev-edition-default"
    },
    [2] {
        IsRelative   1,
        Name         "dev-edition-default-1",
        Path         "Profiles/ab31x35n.dev-edition-default-1"
    }
]

If you need to work with the bookmarks or history, everything is in places.sqlite file. Main list of urls is in table moz_places to which leads foreign keys from tables like moz_bookmarks or moz_historyvisits. Working with the data is quite easy with

use DBI;
use Data::Printer;

my $places_file = shift or die "syntax: read_places directory\\places.sqlite\n";
my $dbh = DBI->connect("dbi:SQLite:dbname=$places_file","","")
    or die $DBI::errstr;

my $history = $dbh->selectall_arrayref(
    "SELECT url, title, description, last_visit_date FROM moz_places ORDER BY last_visit_date DESC LIMIT 1"
);
p $history;

This returns back information about last visited page in the profile

\ [
    [0] [
        [0] "https://support.mozilla.org/en-US/kb/bookmarks-firefox",
        [1] "Bookmarks in Firefox | Firefox Help",
        [2] "Bookmarks are links to web pages that make it easy to get back to your favorite places. Learn the basics of making and managing bookmarks.",
        [3] 1678050000384000
    ]
]

Those profile directories contain much more that the above, see Where Firefox stores your bookmarks, passwords and other user data article on Mozilla support for details.