Blog

Multi-Safari on Leopard

So, after making some research, and solving some web hosting issues for the bigger files, I’m glad to announce Multi-Safari now works directly on Mac OS X Leopard. I’ve added Safari 3.0.4, Tiger and Leopard versions, and updated the Safari 2.x downloads so that they work on Leopard without further tweaking. Older Safari versions (1.x) just can’t work on Leopard, sadly.

So what was the problem exactly with versions 2.x on Leopard? Apple decided they would prevent users from opening older versions of Safari in Mac OS X 10.5.1, possibly as a way to avoid unexpected bugs. Attempting to open an older version of Safari caused an error saying that this application doesn’t work on this version of Mac OS X.

In fact, Safari isn’t the only application for which old versions are blocked, the whole list can be found in the Exceptions.plist file in the LaunchServices system framework. In there you can see that the “com.apple.Safari” minimum version requirement is set to 5523.10, which is the Safari build number you can find in the Info.plist file inside the Safari 3 application bundle. When launching an application with the given bundle identifier, the system checks that the version number isn’t lower than that, and if so prevents the app from launching.

So how did I fix Multi-Safari to work around this system check? Simple: I changed the bundle identifier to “com.michelf.MultiSafari”; that’s all it takes. You may need to duplicate the application (or issue the right command in the Terminal) to make sure the Info.plist’s content is no longer cached by the system.

That causes another “issue” though: Safari, like most Mac OS X applications, keeps its preferences and settings in a file with the same name as its bundle identifier (“com.apple.Safari”), and changing the bundle identifier causes Safari to use another preference file. For commodity, I’ve added to the Multi-Safari launch script a command that copies the “com.apple.Safari.plist” file to “com.michelf.MultiSafari.plist” at first launch1. This means that if you change the regular Safari’s preferences afterwards, it won’t effect preferences for Multi-Safari, and vice-versa.

Last small detail: Mac OS X 10.5 adds automatically a quarantine flag to files downloaded from the internet. To activate this function on earlier versions of Safari which are still supported by Leopard (2.x), I added the “LSFileQuarantineEnabled” key to the Info.plist files. The quarantine function will thus still work Multi-Safari.


  1. Actually, the script does not perform a copy. Instead, it creates a symbolic link which is replaced with a regular file by Safari later, the result is that we have a copy. Open up and look at the script and experiment if you must. ↩︎


Downloading Applications

Lukas Mathis points out that installing Mac OS X applications is often confusing. I can’t agree more. Even as an advanced user I often find installing an application is more hassle than it could be. I realized that a long time ago and that’s why I’m no longer offering disk image downloads anymore.

How it should be

When I click on a link to download a new application, I expect to get an application in my download folder. From there, I should be able to drag it to the trash if I don’t like it, move it to the Application folder (or anywhere else) if I want to keep it, or just leave it there if I’m lazy.

How I’ve done it in the past

In the first incarnations of Mac OS X, Apple told developers to use disk images (those .dmg files) to distribute applications though the Internet. The result is as described by Lukas: you get a disk image, open it to mount a new virtual volume from which you can copy the application elsewhere, or unmount the volume and drag the disk image to the trash. And that volume is automatically unmounted upon a log out.

Well, that’s exactly what I did for my own applications at first. But since I didn’t like that practice much when installing other applications, I was searching for a better way.

Then came Internet-Enabled Disk Images. By setting a certain bit in your .dmg file, you can make the image mounter program treat the disk image as if it was an archive: it mounts the image, copies its content in the same directory your disk image is, and then moves the disk image to the trash. When moving the image to the trash, the internet-enabled bit is cleared, so if you move the file out of the trash and double-click it, it will then mount as a normal image.

I used internet-enabled disk images for my software only for a short time. As a developer, I feel they’re tricky to manipulate since they loose their internet-enabled bit just by double-clicking them.

Internet-enabled disk images provide the ideal experience described above only if your browser is set up to open the file automatically after download. By opening the disk image file with the appropriate helper (the system’s disk image mounter), the browser would provide that ideal experience and all you’d see after downloading is the application you wanted. Alas, for security reasons, few browsers do that today, and when you open manually the file you downloaded, that file disappear as a new one appears beside it, which is somewhat confusing.

Then, Safari came along and changed a few things.

How I’m doing it now

Safari’s handling of compressed files is somewhat different from other browsers. Upon downloading a zip archive, or an internet-enabled disk image, Safari automatically decompress the content it and put the archive to the trash (this behaviour can be disabled from Safari’s preferences). Remember the ideal scenario depicted above? Well, that’s exactly it: you click on the download link for the application and you get the application in your download folder, plain and simple.

Since Safari appeared, I’ve been distributing my apps in zip1 files. From the developer-preparing-a-file side, you can create the archive directly from the Finder and you’re guarantied that double-clicking it won’t disable the automatic unarchinving (as with internet-enabled disk images), so it’s safer to manipulate. Also, disk images are a little tricky to host on a web server too because the server often doesn’t know about the .dmg file extension and can’t label it with the proper MIME type; no such problem with zip files.

As a user, if you have disabled automatic decompression, or are using another browser which doesn’t automatically decompress, you’ll get a simple archive you can manually decompress and put to the trash if you want too. The downloaded file won’t go to the trash by itself and become a non-internet-enabled disk image in the process (something which I find very counter-intuitive).

Conclusion

Thanks to Safari, the ideal scenario works quite well, as long as you’re using one of the right file formats.

I still don’t get why some applications are still distributed on the web with non-internet-enabled disk images. Don’t developers realize they can do better for their users? Having a flashy window pop in the Finder with instructions for installing the app is more hassle for your users than just having the application appear in the download folder.

Or perhaps developers fear the user will forget they downloaded their app if when the download finish it doesn’t pop a new window with their logo in big. If that’s the problem, then perhaps Safari should stop opening non-internet-enabled disk images by itself when the download finish… In fact, now that I think about it, I wish Safari wouldn’t do that.


  1. Note that, since Mac OS X 10.3, the system unarchiver can also decompress gzip- and bzip-compressed tar archives (.tgz and .tbz files); those will get you better compression ratios than zip files and will be handled perfectly well by Safari. I’ve already began using .tbz files for things that can’t work on 10.2 anyway (mostly developer stuff). ↩︎


Update on Multi-Safari

It’s interesting how many people became suddenly interested by Multi-Safari in the last few weeks. In the last few weeks, Apple released Mac OS X 10.5 Leopard, with Safari 3, and they released the 10.4.11 update to Tiger, also with Safari 3, leaving a lot of people wondering how to test websites under previous versions of Safari.

It seems the self-contained Safari applications works fine under Leopard… that is, unless you happend to update to Leopard 10.5.1, in which case you’ll get an error message telling you that this version of Safari doesn’t work on Leopard. I don’t have Leopard myself so I’m telling you this based on all the reports people have sent to me by email. More importantly, since I don’t have Leopard, I can’t investigate right now to find out what is causing this and see if there is a solution.

But having said that, it seems odd to me that suddenly, in a minor point release, an application stops working. Perhaps it’s trigging some security feature which wasn’t working in the early Leopard release, or perhaps Apple deliberately added a block for older versions of Safari (which would make some sense since in general running an older Safari on a newer system isn’t a so good idea). If someone has a clue, I’d like to hear it.

In the meanwhile, you’ll have to resort to using a Tiger (10.4) system to run Safari 2, or just refrain from updating to Leopard 10.5.1.


Playing with Xcode internals

After releasing version 0.1 of the D/Objective-C bridge, I decided to take some time to improve my development tools to better support the D language. I’m using Xcode, and so I want Xcode to play well with D source files. Here’s the story of how I ended up creating the D for Xcode plugin, which goes beyond what any other Xcode plugins I’ve seen in term of integration.

Unsurprisingly, Xcode doesn’t compile D by itself. You can make things work using shell scripts and custom build rules, but that’s tedious and you’ll have to manually add these scripts to each one of your projects you want to support D. Better yet, you can create a compiler specification file which tells Xcode how to issue compiler commands to compile specific file types. This is how Alan West made his Xcode D plugin.

The Dependency Graph

The problem with Alan’s plugin is that it doesn’t track dependencies between files. For instance, if you have file A which imports file B, if you change file B then you need to recompile file A too. Xcode handle that automatically for C, C++, and Objective-C by scanning the source files for any #include and #import directive and creating a dependency graph, but Alan’s plugin doesn’t have a parser to find import directives in D files and thus Xcode cannot know which file depend on which.

So I needed a parser to find import statements in D source files. The first thing I did was to take the the Digital Mars’ D front end (which is used by the DMD and GDC compilers), take the lexer class (a tokenizer) and implement a simplistic parser that only scan for import statements. Then I based myself on Damien Bobillot’s work which details the Xcode plugin interface (because the Xcode API is not public) to create a compiler specification class integrating that small parser.

Syntax Highlighting

Then I though it’d be a good thing to use the lexer I’ve just brought in from the D front end to improve syntax highlighting. Alan had created a language specification file to tell the Xcode lexer how to highlight things, and I did the same thing before I learned about Alan’s plugin. But this approach is limited enough: Xcode’s own lexer is just too simplistic to handle all of the D syntax (like nested comments, raw strings, binary numbers, etc.).

Fortunately enough, Damien did create the required headers to create a lexer for Xcode (which handle syntax highlighting). However, how to use this part of the API hasn’t been documented by anyone (that I know of) yet, and I was mostly on my own. With some trial and errors to see exactly what Xcode was doing when I gave it a custom lexer class, I figured out how to create a useable lexer.

With more work, I successfully wrapped the D front end’s lexer into a lexer class for Xcode. The main issue here is that Xcode works with UTF-16 characters while the D lexer expects UTF-8 input. So I had to reorganize the lexer on the D side so that it count characters not bytes, and I’m stuck with doing a UTF-16 to UTF-8 conversion each time Xcode invokes the lexer lexer (on every keystroke in short). Moreover, to this date, the D front end lexer can only process a file on its entirety; it can’t, say, start again from a given state at a given place and stop before the end. So on every keystroke, Xcode invoke the lexer which reprocess the entire file. That’s not very efficient, but, while using it on my iBook, I can’t really sense the difference, so it’s probably good enough for a first version.

The Editor Pop-Up Menu

The next thing I wanted is to enable Xcode’s editor pop-up menu to go to specific parts of the document. This menu associate functions and classes to specific parts of the document so you can look at it to know where you are and you can open it and select a destination to go somewhere else in the document. Needless to say, this requires a near-complete D parser, something which I had at hand in the D front end.

Basically, this was the same process as adopting the D lexer: create a source scanner class for Xcode that pass things to the D parser, and create and return to Xcode a suitable tree, and “arrange” the parser so that it remembers the specific character ranges for for symbols and scopes, but in the end this wasn’t too hard.

Other Things

Xcode’s debugger wouldn’t allow breakpoints to be set in D files. That was until I try to set the D filetype a superset of “sourcecode.c”. While not technically correct (D isn’t a superset of C), this setting doesn’t seem to cause any problem and it allows breakpoints to be set within Xcode.

Conclusion

So here you have it, the better D plugin for Xcode. If you haven’t it already, you’ll want the GDC compiler to try it out: go to gdcmac and grab it. (You’ll need to take the patched GDB in addition to GDC if you want the debugger to work best with D code.)

And now, I can restart working on the D/Objective-C bridge and get a top-notch development experience with Xcode. I hope you’ll too.



  • © 2003–2025 Michel Fortin.