Thursday, December 30, 2010

dyld: unknown required load command 0x80000022 starting Firefox (and others) on Mac OS X 10.5

If you get a message like this when running a Mozilla application on Leopard:
dyld: unknown required load command 0x80000022
Trace/BPT trap
...chances are the system tries to run a build intended for Snow Leopard.

A workaround you could try is to run Terminal.app and use the arch command like this:

arch -i386 /Applications/Firefox.app/Contents/MacOS/firefox-bin

(Substitute /Application/Firefox.app with the location of the app, and use the name of the executable (ending with -bin) instead of firefox-bin as necessary.)

As with any other command you can create a launcher icon for the command easily.

This should apply to newer Mozilla-based applications: Firefox 4 (including Minefield builds since Oct 2010), Thunderbird post 3.2, BlueGriffon, and future versions of other applications based on Mozilla 2 (Komodo, Songbird, etc.)

Technical details

If the workaround works for you, the application is a i386/x86_64 universal binary. The x86_64 part of the application uses 10.6-specific features, so it can't run on 10.5; the i386 binary is intended for 10.5.

The cause of the error message is that the wrong architecture is selected for some reason. This happened to me when trying to run such an executable directly from the command line (technical details are in this Mozilla bug).

Stripping x86_64 part from the binary

This tip is brought to you by JW from the comments (via jarib from github): if you don't control the command line command used to start Firefox, you can instead strip the x86_64 part from firefox-bin using ditto:

cd /Applications/Firefox.app/Contents/MacOS
mv firefox-bin firefox-bin.original
ditto --arch i386 firefox-bin.original firefox-bin

Warning: with this workaround (unlike when running via arch) you will stop receiving Firefox updates and this change will have to be re-applied each time you update Firefox manually.

Wednesday, May 26, 2010

Couldn't load XRE functions with XULRunner on Mac OS X

While trying to upgrade XULRunner and ChatZilla on my Mac OS X (10.5), I got into a state where running ChatZilla.app didn't have any effect.

When I tried to run /Applications/ChatZilla.app/Contents/MacOS/xulrunner in the terminal, I got "Couldn't load XRE functions."

Going into /Library/Frameworks/XUL.framework/Versions/ and deleting the old 1.8 version of XULRunner fixed this (I don't know why, though).

Sunday, March 28, 2010

Jetpack/Addon SDK and XUL extensions

Update [2011-10]: New links to the fork of the Add-on SDK with support for XUL extensions and the documentation.

Update [2011-03]: See the fork of the Add-on SDK with support for XUL extensions and the updated documentation. I'm hoping to get the changes into the main Add-on SDK repository soon.

Update [2011-01]: These steps no longer work with the newer SDK versions. I'm working on making the SDK work with XUL extensions again. It's not finished, but you're welcome to try it out.

Update: These instructions were included in the Jetpack SDK documentation. Refer to the "Using the SDK with XUL extensions" section of the documentation for the up to date instructions for your SDK version.

I played around with the recently released Jetpack SDK. Currently at version 0.2, the SDK doesn't introduce many new APIs, focusing instead on the infrastructure: implementation of the CommonJS Modules spec and command-line tools to run, unit-test, and package Jetpacks as XPIs.

I find this functionality interesting for XUL-based extensions as well, so I wanted to try using it with my existing "old-style" extension (InfoLister).

Getting started

It turned out to be easy:

  1. Get the SDK source with
    hg clone http://hg.mozilla.org/labs/jetpack-sdk/
  2. cd jetpack-sdk
    . bin/activate
    mkdir packages/my-extension
  3. Copy the extension template the SDK uses to run jetpacks to your own folder:
    cp -R python-lib/cuddlefish/app-extension packages/my-extension/extension
    There's only one interesting file (as of SDK 0.2) in the template extension - the harness.js component that provides the CommonJS module loader (i.e. the require() implementation) and bootstraps the Jetpack (i.e. starts its main program or runs tests).
  4. Copy your other extension files to packages/my-extension/extension (components, chrome.manifest and chrome files, etc.)
  5. Create packages/my-extension/package.json (described in the introductory tutorial and the detailed Package Specification):
    {
      "id": "infolister@nickolay.ponomarev",
      "description": "Lists installed extensions and themes",
      "author": "Nickolay Ponomarev (asqueella@gmail.com)",
      "version": "0.11",
      "license": "MPL/GPL/LGPL"
    }
    The specified id is important, as it becomes the packaged extension's ID and is used to obtain the module loader below.
  6. Create directories for your CommonJS modules and tests, and the main module for cfx run:
    mkdir packages/my-extension/lib
    mkdir packages/my-extension/tests
    // packages/my-extension/lib/main.js
    exports.main = function(options, callbacks) {
    //  callbacks.quit();
    }
    Since we're just trying to get an old style-extension launched, we don't do anything in main() yet.
  7. Now we can run Firefox with a clean profile and our extension installed using:
    cfx run -a firefox -t extension
    -t extension tells cfx to use the directory we created earlier to install the extension.
  8. Similarly, to build XPI of the extension we can use
    cfx xpi -t extension

(cfx is a python script that uses mozrunner to create the test profile and launch the specified Mozilla application. It would be interesting to teach it to set up an advanced development profile (and re-use it instead of recreating it each time).)

Using CommonJS modules

Now that we have our extension running along with Jetpack core, we can start using modules in our code.

CommonJS modules are a neat idea: it's a standard way to write pieces of JS code that:

  • are isolated from each other (each module gets its own global object);
  • have explicitly defined "exported" properties (via exports) and imports (via require());
  • can be shared and reused by other developers, theoretically even across platforms (e.g. there can be modules used by both Mozilla-based jetpacks/extensions, website code, and even other non-browser platforms).

Jetpack SDK also provides a standard way to unit-test modules and a nice documentation browser for them, which will likely get even more useful with time.

Since the SDK tutorial has enough details about testing and documentation of modules, let's see how the modules can be accessed from a regular extension. We have already created a main module earlier, so how do we access it from the extension?

The answer is to use the loader from the harness service, which we mentioned earlier:

function loadJetpackModule(module) {
  return Components.classes["@mozilla.org/harness-service;1?id=infolister@nickolay.ponomarev"].
    getService().wrappedJSObject.loader.require(module);
}
alert(typeof loadJetpackModule("main").main); // ==> function

Friday, January 29, 2010

Using Firefox profiles on Mac OS X

If you are a Firefox power user, you are probably familiar with its profile system. A profile is a folder where Firefox keeps all your customizations, history, bookmarks, cookies, and other data.

Firefox lets you run several independent instances using different profiles simultaneously -- by specifying special options in the command line. The basics are covered in this lifehacker article.

Unfortunately on Mac OS X there's no easy way to specify command line parameters in a "shortcut", like there is on Windows. The well-known workarounds are:
  1. Use Terminal.app or similar to execute /Applications/Firefox.app/Contents/MacOS/firefox-bin with the necessary parameters.
  2. Use a separate launcher application like MultiFirefox, which is similar to Firefox's own profile manager, and also lets you choose the Firefox version to run.
  3. Use Script Editor.app to create an "application" that runs Firefox with the command-line parameters you want.
  4. Create multiple slightly edited copies of Firefox.app that run with the specified profile.
I used a combination of these until recently, and they're all imperfect: the first and second workarounds require several steps to run Firefox, and the third option was too slow for me. The fourth just seems wrong.

My solution

This solution was tested with Mac OS X Leopard, Snow Leopard, and Lion. We create a lightweight bash-based application, which starts Firefox with the right command line arguments.

You can download a zip with the template application, which should show the profile manager when run (assuming you have Firefox installed to the standard location).

Here's how you create this application:

  1. Create a folder /Applications/FirefoxWork.app with a folder Contents inside it. (To open an *.app folder in Finder use the context menu Show Package Contents.)
  2. Inside the Contents folder create a file named Info.plist (note: all names are case sensitive!) with the following contents:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
      <key>CFBundleExecutable</key>
      <string>FirefoxLauncher</string>
      <key>CFBundleIdentifier</key>
      <string>com.example.FirefoxLauncher</string>
      <key>CFBundleInfoDictionaryVersion</key>
      <string>6.0</string>
      <key>CFBundleName</key>
      <string>FirefoxLauncher</string>
      <key>CFBundlePackageType</key>
      <string>APPL</string>
      <key>CFBundleSignature</key>
      <string>????</string>
      <key>CFBundleVersion</key>
      <string>1.0</string>
    </dict>
    </plist>
    (the meaning of these keys is explained in the Core Foundation Keys reference).
  3. Inside Contents create another subfolder named MacOS.
  4. Save the following to a file called FirefoxLauncher in the MacOS folder:
    #!/bin/bash
    /Applications/Firefox.app/Contents/MacOS/firefox-bin -no-remote -P work &
    Adjust the path to Firefox and the profile name as appropriate.
    Update: You may need to prepend arch -i386 to the command if you're trying to run newer Firefox (version 4 and later) on Mac OS X 10.5.
    Update 2: (thanks to Daniel Beck on superuser.com for this tip) you can use open instead to avoid hardcoding the path to the application and the binary name:
    open -n -a Firefox.app --args -no-remote -P work
  5. Run chmod u+x /Applications/FirefoxWork.app/Contents/MacOS/FirefoxLauncher in the terminal to make the script executable.
  6. Now test if the program runs by executing open /Applications/FirefoxWork.app.
    • If you get an error saying "LSOpenFromURLSpec() failed with error -10810 for the file /Applications/FirefoxWork.app.", most probably the script couldn't be run - either the name in CFBundleExecutable parameter didn't match the actual script name in MacOS or the file doesn't have the execute bit set (see chmod command above).
      If you edited Info.plist, be sure to touch FirefoxWork.app to invalidate OS X's cache. 
  7. Voila! You can now drag FirefoxWork.app to the dock or run the application via QuickSilver and it doesn't take a second to run the shell script.