Update: Bug 253742 is very close to being fixed, so the workarounds in this post will no longer be needed. Thanks to the Mozilla developers for making the platform even better.
Every Firefox extension consists of several parts. It often has a UI part, typically written in XUL and some scripts typically written in Javascript. More advanced extensions have compiled XPCOM components in form of .dll or .so files.
While the XUL and the Javascript parts can often be used on all platforms (Windows, Linux etc.) without modification, the XPCOM components need to be platform specific (for obvious reasons).
I wanted to have a single extension package (XPI), which included the XPCOM components for all the platforms. When the extension is installed, it should detect the user platform and load only the appropriate components.
The first thing I noticed is that the extension manager mechanism doesn’t allow conditional installations (install different components depending on user’s platform).
Next, I tried the following reasoning: package both .dll and .so files with the extension. Firefox on Windows will load only the .dll files while ignoring the .so files, and on Linux only the .so files, while ignoring the .dll files. It was only logical to me that the Windows version of Firefox will not try to load the .so files.
It didn’t work. Windows Firefox did try to load the .so file and popped up and ugly message box saying something about wrong file format. Looking through Mozilla source code, I saw that there is a list of all the dynamic libraries extensions (.dll, .so etc.) and Mozilla will try to load all the file types on all the platforms.
After posting a question on the mozillazine.org forums, I got an answer saying that I should provide several different packages for the different platforms. I wasn’t ready to go that way. Managing several packages, when all that is different between them is a single file seemed unreasonable.
So I came up with a work-around. I had only Windows and Linux versions, but the technique can be extended further to support more platforms. Here it is:
- Package the Windows version of the component as
MyComponent.dll
- Package the Linux version of the component as
MyComponent.dll.linux
- Windows will load the .dll file while ignoring the .dll.linux file (because its extension is not a valid dynamic library extension). Nothing else should be done here.
- On Linux – the first time the extension is loaded, it will try to load
MyComponent.dll file and fail (because the file is a Windows DLL). It will fail gracefully, without popping up any message boxes.
- During the startup of my extension I check whether the user platform is Linux and if
MyComponent.dll.linux file exists.
- If so, this means that this is the first run. The script moves (renames)
MyComponent.dll.linux to MyComponent.dll, removes the components registry files, compreg.dat and xpti.dat, so they are rebuilt the next time Firefox is started
and inform the user that an additional restart is needed.
- When the browser is restarted, the
MyComponent.dll is loaded successfully (because it is now a valid .so file) and the registry files are rebuilt to reflect its contents.
- The extension is now fully functional.
One little question needs to be answered – why do I rename MyComponent.dll.linux to MyComponent.dll and not to something more Linux-like as MyComponent.so.
The answer is – uninstall. If I created a new file named MyComponent.so, when my extension got uninstalled the file wouldn’t have been deleted, because it was never installed. Moving MyComponent.dll.linux to MyComponent.dll leaves all the files with exactly the same names, so when uninstall comes, they are removed.
I can provide the complete source code (in Javascript) for this technique on request.