DevGypsy

Berlin and fixing Firefox in Cookie

I ended up spending 2 months in Bucharest, and was super productive while I was there - managing to release a number of Cookie 6 beta builds. The final version was released around a month ago, after spending the better part of this year working on it.

Berlin

I began the summer performing at a couple of street festivals in Austria. Then, afterwards, as I had no where I needed to be - I figured I might as well head up to the Berlin Lacht festival and hang with some friends. I hit up a friend, and ended up staying at his apartment in the Kopi squat for a round a week. During the week I was there, I ended up meeting a girl, so of course I had to find myself an apartment to see where things would end up. Well, it’s 2 months later and I’m still here in Berlin and still spending time with this fantastic woman 🙂.

Problems reading Firefox data

So, I was getting reports that Firefox wasn’t working for a number of users. I had no idea why, as I just could not recreate the issue locally. Luckily, someone who was having the issue sent me a copy of their profiles.ini file. And as soon as I opened it, I saw the problem. Firefox had changed its default profile naming scheme… o no… I had taken the path of least resistance when adding support for Firefox, and had hard coded the profile names. oops… I guess it was time to future proof my code. Easier said then down, Firefox uses (I think) quite a convoluted way of keeping track of which version of Firefox (Beta/Nighty/Developer Edition/Stable)

Firefox profiles.ini file

Here’s a sample profiles.ini file:

[Profile0]
Name=default-nightly
IsRelative=1
Path=Profiles/fg76d9as.default-nightly

[Profile1]
Name=default
IsRelative=1
Path=Profiles/6gag97mb.default
Default=1

[Install31210A081F86E80E]
Default=Profiles/fg76d9as.default-nightly
Locked=1

[General]
StartWithLastProfile=1
Version=2

[Profile2]
Name=default-release
IsRelative=1
Path=Profiles/0imkg63b.default-release

[Install2656FF1E876E9973]
Default=Profiles/0imkg63b.default-release
Locked=1

Pretty straight forward really. This file has 3 profiles:

  • Profile0
  • Profile1
  • Profile2

And the Installxxxxxxxxxxxxxxxx entries are obviously references to the 2 separate Firefox installs and the profile locations to which they are using.

So far so good, now I just needed to find out how to match up which version of Firefox corresponds to which Installxxxxxxxxxxxxxxxx entry. This was easier said than done… I ran some searches on StackOverflow and DuckDuckGo and quickly discovered that the random string was in fact a hash of the Firefox install. OK nice, but how exactly does the hash reference the Firefox install. Was it a hash of the name? the path? or a combination of both of these variables… I needed to dig into the Firefox source code.

Making sense of the Install hash

So, I downloaded the latest source, did some grepping, and came across a promising code block appropriately named: GetInstallHash() Heres the interesting part of that function:

nsresult GetInstallHash(const char16_t* installPath, const char* vendor, mozilla::UniquePtr<NS_tchar[]>& result, bool useCompatibilityMode /* = false */) {
MOZ_ASSERT(installPath != nullptr, "Install path must not be null in GetInstallHash");

// Unable to get the cached hash, so compute it.
size_t pathSize = std::char_traits<char16_t>::length(installPath) * sizeof(*installPath);
uint64_t hash = CityHash64(reinterpret_cast<const char*>(installPath), pathSize);

So, it seems the hash is computed by feeding the absolute path of the Firefox binary into this function, then running it through another function: CityHash (whatever that may be).

CityHash

Back to DuckDuckGo, and I discovered that CityHash is an opensource hashing algorithm from Google. Nice. I grabbed the source, and monkey wrenched it into an alpha build of Cookie, along with my own implementation of the GetInstallHash method from Mozilla. Now I could feed in the location for any Firefox (binary) installs on a users computer and get a hash spit out the other end - which would hopefully match a Profile to a location in the Profiles.ini file.

Not quite there yet

Unfortunately the hashes produced were nothing like the hashes in the Profiles.ini file… It looked as though they were encoded in uppercase Hexidecimal. No problem, I added a hexadecimal converter to the output hash. Still the hashes were different to what I was expecting…. back to the mozilla source code, and I soon discovered that Firefox wasn’t using the latest version of CityHash. As soon as I updated (downgraded actually, to v1.0) my CityHash source to the same version as used by Firefox - the hashes matched!

Done

Once I had a way of recreating the Installxxxxxxxxxxxxxxxx entry in the Profiles.ini file, it was pretty trivial to get the correct profile folder location for each Firefox install. Cookie was working reliably again. 🙂