OSXtreme
  Software - Chimera Custom - Development Tips

This is my list of tips for anyone that wants to contribute to Chimera. Might be useful for other projects, too, but this is what I learned trying to work on Chimera.

Some terms, conventions, and assumptions

  • You want to change Chimera in some way, and you probably want contribute your change to the Chimera team.
  • If you aren't comfortable in a UNIX shell, you might need to brush up on some skills, because this project is best worked on with command line tools. If you don't know that "~" means your homedirectory, get a book or two (the Missing Manual book has a decent UNIX/Terminal section to get you started)
  • You have the Apple developer tools installed (in /Developer) If you need it, get yourself an ADC account and download the huge installer (at least it's free!).
  • Concerning build styles/trees: optimized and deployment mean the same thing: A smaller, faster, build that produces less output (to console). The other kind of build is called debug or development, which means a larger application that has diagnostic output.

Setting up your development trees

I tend to keep three source trees: a debug tree for development, an optimized tree for testing and packaging multiple patches together for my own customized Chimera build, and a "clean" tree for making patches against. (It doesn't really matter if your clean tree is debug or opmitmized, the code is the same. The point is to have it as clean as possible) I followed the directions here to build each tree. You only need to install fink and Shared Menus once, of course.

I made a directory in my homedirectory called "m" because I'm lazy, and did my debug checkout there. (follow the directions and don't do the "optimized" steps for .mozconfig) So, my debug tree lives in ~/m/mozilla/chimera. I then created ~/m/mozilla-opt, and did an optimized checkout there (added the extra two lines to .mozconfig). Takes 1-2 hours to do a full build and checkout.

Then, to keep a clean copy for making patches, I copy mozilla-opt to mozilla-opt-clean via tar. Example:

cd ~/m
mkdir mozilla-clean-opt
cd mozilla-opt
tar -cf - . | (cd ../mozilla-clean-opt && tar -xvf - )
This takes a fair amount of disk space: a built debug tree is around 2.5GB, and a built optimized tree is upwards of 500-600MB.

Keeping current

Since the body of code is changing all the time, you need to manage when and how to integrate those changes. The model I like, involves doing all development, testing, and debugging in the debug tree.

Then, make a patch from it, and hand-edit it to take out things that don't belong (extra whitespace, debug print statements that don't need to be there, features you didn't realize snuck in while you where coding away like mad ;) See below, Supplying a patch, for how to make a good patch file.

This patch will be used to save your changes and put them into the more current tree - since development on a feature can take some time, and you might not want to constantly be running CVS updates and resolving conflicts, I save that for one sweep - that's next.

You want a current, completely clean, optimized tree. I keep a clean-opt tree around for this purpose. There's three levels of "updating" this tree, in simple to complex/long order:

  • Simplest: cd mozilla/chimera ; cvs update
    This won't pull down new directories! So, that's bad. You can use cvs update -d, but it's recommended to at least go to the next level.
  • Normal: cd mozilla ; make -f client.mk fast-update You need to wait for various levels of mozilla to recompile, now.
  • Full update: cd mozilla ; make -f client.mk pull_all This will make sure everything is in sync, and rebuild stuff. Can take 20-30 minutes or so.
  • Start over from scratch: do a full checkout into a new directory. This is a 1-2 hour process for me.
In any case, whenever you do any kind of update, make sure to compile and test the application before you make any of your own changes, you need to make sure problems you find later are your own fault :)

See the CVS docs for what the output of cvs update means.

At this point, I usually save off the mozilla/chimera tree into mozilla/chimera-clean-opt, and make a copy into mozilla/chimera (just like the tar commands, above) so I can keep playing with it.

Then, apply your patch to the clean-opt tree, being careful to watch for conflicts that need to be managed. The patch program tries to merge your changes in, but it can run into problems and give up, and you need to go take care of those. The message you'll see is something like "Hunk failed".

Build it with 'make', and ensure it all still works as you intended. Now that you have your changes in a more current CVS tree, make the real patch that you will distribute/keep.

If you are keeping a custom version of Chimera that has several patches in it, apply your new patch to that and test it. In any case, you probably want to go to a clean chimera tree and apply it to be sure it's a good patch.

CVS

It's worth learning some CVS, in addition to the Setting up your environment and Keeping current sections, above, see Supplying a patch, below.

You probably need to be able to do the following:

  • Do a checkout (see above)
  • Keep your tree current (see above)
  • Undo a change you've made (remove the file and cvs update it)
  • Make a patch/diff (see below)
  • Remove a checkout (see below)

For more info:

Work environment

I actually like working in Project Builder. You can open mozilla/chimera/Chimera.pbproj/ and it will start Project Builder for you. As fair warning, this causes some funny issues. First, it creates some files, which store your windows and settings and suchlike; this means you probably don't want to create patches from this tree (unless you want to prune the extra files by hand from the patchfile).

Second, the Project Builder files are hard-coded to do a Development build - this means you absolutely do not want to hit the Build button unless you are making a debug application! If you do, you will have to go into mozilla/chimera and run make clean and make by hand -- if you try to mix optimized and debug, you will get a link error.

Interestingly, the converse also seems to be true: if you are getting a link error, you are likely trying to mix them. This is why you need separate entire mozilla trees - that has to be compiled the same way, to match all of the Chimera binaries. If you are having link errors, cd mozilla/chimera and run make clean ; make. If you still have errors, you might want to cd mozilla and run make clean; make -w -f client.mk (and/or look in .mozconfig and verify things are setup as expected). If you still can't make it work, do a fresh checkout.

Likely, the cleanest tool for editing the .mm and .h files is emacs. The Chimera files all have hints to emacs about how to handle tabs and the like - since whitespace is generally annoying to CVS, there's a convetion, and it's best to follow it. (namely, tabs are not to be used; you should use two spaces for indentation). I use ProjectBuilder anyway, because it's easier to find files, it works for editing Localizable.strings, and you can double-click .nib files to open them in InterfaceBuilder

Compiling

If you are making a debug version, and working in Project Builder, I recommend the Build button there - it tracks errors and shows you problems more efficiently. If you are making an optimized version, make sure not to use Project Builder. Instead, run make from mozilla/chimera.

Getting rid of a build tree

CVS is very forgiving. You don't need to run 'cvs release' to let go of a checkout, you can just remove/trash it. Don't bother with any cvs add or cvs commit commands, either; you won't be allowed to.

Related Resources

mozilla.org: mozdev.org: Other:

Bugzilla

For starters, be nice. Chimera is part of a large open source project, Mozilla, so there's lots of people from around the world with many different view points. And keep in mind that (as far as I know) almost none, if any, of the people doing this work are being paid for it - it's a wonder that Chimera exists at all, really.

Everything that will go into Chimera gets a bug, so you should learn the system. It will also tell you about things that other people have already reported, which will save you time trying to track down or understand a problem you are running into. This also makes a great place to find out what things need working on. Find a bug that sounds interesting, that doesn't have any/recent progress made on it, and that people want fixed.

When filing bugs, make sure to look for an existing bug first. You don't just want to get marked as a duplicate, do you?

Your basic two types of bugs are, well, Bugs and RFEs - Requests for Enhancement, like new features. You can tell this from the Severity (anything that isn't an 'enchancement' is a Bug, to my oversimplified view). If it's a Bug, and you fix it, I'd guess you have a good chance of getting your code in, especially if you don't cause any more problems or need any UI changes. Enhancements are special bugs, because they aren't really required - some people might think they are, of course. Working on an enhancement is tricky because it might be an entirely new feature that won't make it into the final product. You can still use it yourself, of course, if you're willing to keep downloading the source and applying your patches. This is why it's important to put your work in a patch, regardless of whether you think it will be accepted - so you can keep using it.

(Needs work. I should supply some of my query lists.)

Examples of changes

Changing the Main Menu

Here's how I added my MainMenu changes for the Save State feature
  • Open MainMenu.nib
    • command line: open mozilla/chimera/resources/localized/English.lproj/MainMenu.nib
    • Project Builder: double click Resources->Nibs->MainMenu.nib
  • In the Main Menu window, click Window to open it
  • From the Palettes->Cocoa-Menus, drag three "Items" and some separators (the blank thing)
  • Double-click each Item to rename it (Save Window State, Open Saved Windows, Open Last Autosaved State)
  • Double-click the hot-key area on the right side to assign hotkeys (control-cmd-S and control-cmd-O for Save and Open Saved)
  • In the MainMenu.nib window (not the one with the menu items in it) in the Instances tab, double-click MainController.
  • In the Attributes drop-down, Actions tab, hit Add to add each of saveWindowState: openWindowState: and openLastAutosavedWindowState:
  • Control-drag from each menu item to the MainController instance, select the appropriate action, and hit Connect
  • Save or exit Interface Builder
  • tar -zcvf MainMenu.nib.tgz resources/localized/English.lproj/MainMenu.nib

Adding a preference item

This is an example of adding a new Checkbox to WebFeatures.nib, and enabling it in the code.
  • WebFeatures.nib
    • double-click instances, file's owner. add an Outlet (mForceWindowOnRestore) of type NSButton add an Action (clickForceWindowOnRestore)
    • in the window itself, copy/paste an existing checkbox and re-title
    • control-drag from Instance File's Owner to checkbox, connect to Outlet
    • control-drag from checkbox to File's Owner, connect to Action
    • File's Owner, LastKeyView, disconnect; Control-drag from File's Owner to new last element
    • For previous last element, drag to new element and make nextKeyView, and set new element's nextKeyView to the first element
    • save
  • UserDefaults.h
    • Add a key for this preference, #define USER_DEFAULTS_FORCE_WINDOW_ON_RESTORE @"Force Window on Restore" /* Boolean */
  • WebFeatures.h
    • Add IBOutlet and IBAction needed
  • WebFeatures.mm
    • Make sure to #include "UserDefaults.h"
    • In the initialization, mainViewDidLoad:, need at top:
      NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    • Then set initial state:
      [mForceWindowOnRestore setState:[defaults boolForKey:USER_DEFAULTS_FORCE_WINDOW_ON_RESTORE]];
    • Turn the preference on or off when clicked:
      - (IBAction)clickForceWindowOnRestore:(id)sender { NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; [defaults setBool:[sender state] forKey:USER_DEFAULTS_FORCE_WINDOW! }
  • MainController.mm
    • Use the preference, i.e.: if ([defaults boolForKey:USER_DEFAULTS_FORCE_WINDOW_ON_RESTORE]) { [self newWindow:self]; }

Making your own Preference Pane

Making Custom Preferenence Pane by copying WebFeatures:
  • Setting up the files/directories:
    • mkdir PreferencePanes/Custom
    • cp WebFeatures.{mm,h,tiff} to Custom.
    • mkdir English.lproj
    • Open WebFeatures.nib in IB, save as Custom under English.lproj Don't add to PB project.
  • Setting up project builder (likely need to do every patch)
    • PB, Targets sidebar, duplicate WebFeatures, rename to CustomPrefPane, and rename all WebFeatures items inside Settings and Info.plist Entries.
    • With CustomPrefPane as build target, back in Files sidebar, select PreferencePanes, and then "Project->Add Files"
    • pick the PreferencePanes/Custom directory, and accept defaults (don't copy items, reference default, and recursive) and add to Target CustomPrefPane
    • Then remove all WebFeatures files from the target (either in the Files sidebar, or click on them and right-click->delete from the Targets pane)
    • With target Navigator and NavigatorStatic selected (each), use the Files sidebar on Products/Custom.prefPane's checkbox to add to those.
    • Find the "Copy Files" part of the Navigator and Navigator Static Targets->Build Phases, and drag the Product/Custom.prefPane from the Files sidebar. (For me, the 14th Copy Files Phase has all the other PreferancePanes)
    • In the Targets sidebar, drag the CustomPrefPane Target into/under the Navigator and Navigator Static targets to set up the dependency
    • Note that to build this, will need a UserDefaults.h that has all the associated #defines in it.
  • Change the Custom files to reflect the Custom class
    • Interface Builder:
      • Double-click File's Owner and change classname Remove unneeded Outlets and Actions
      • Cntrl-Drag from Window to first button, setup FirstReponder
      • Cntrl-Drag from each element to the next, set nextKeyView File's Owner, disconnect FirstKey, InitialKey, and LastKey, then cntrl-drag to the first, first and last items
    • Project Builder:
      • Edit the .mm and .h files to be the new Custom class
      • Edit PreferencePanes/PrefWindow: MVPreferencePaneDefaults.plist MVPreferencePaneGroups.plist

Changing Localizable.strings

Make sure to edit this in Project Builder, or something else that will play nice with the UTF encodings. This is a binary file, so cvs diff will not put changes into a patchable format; for a patch bundle, you should provide both your copy of the file and a description of your changes in a text file.

Changing key bindings

You can change key bindings in Project Builder, but your best bet is actually to make a change in your org.mozilla.navigator.plist. Check out the defaults command. An example of mapping Quit to something complex (Control-Shift-Cmd-Q, so that I don't accidentally hit it), and mapping Home to Cmd-M:
        <key>NSUserKeyEquivalents</key>
        <dict>
                <key>Home</key>
                <string>@m</string>
                <key>Quit Navigator</key>
                <string>^$@q</string>
        </dict>

Dealing with preferences

The preferences are kept in prefs.js and org.mozilla.navigator.plist. In my opinion, prefs.js should just be for the mozilla-gecko engine, and Chimera UI preferences should be in the .plist. So, changes I make all end up in the .plist.

prefs.js lives in ~/Library/Application Support/Chimera/Profiles/default/[some string].slt/. There's a lot of prefs.js settings. Look for programs like SpeedChimera for some examples, or try about:config (this works in Mozilla, but not very well in Chimera, which should maybe be fixed...) Some people say you should only edit user.js, I'm not sure on this, but I thought I'd mention it. efritz.net has a list of tricks, as well.

org.mozilla.navigator.plist lives in ~/Library/Preferences/. For the .plist settings, it's an XML file, you can edit it with a text editor, Project Builder, or the /usr/bin/defaults command. This is handy when you want to make a change that can be turned on/off but don't want to deal with changing the UI elements. As a matter of style, I recommend that all code changes default to off, and that having it off means behaving like the normal Chimera. Someone can decide later to turn your feature on by default, if that's desired (or even remove the preference and hard-code it on, depending on the feature).

However you want to do the preference, I recommend at least having one, so you can easily test the effect of your code. This isn't practical in all cases, but it gives you better flexibility, and may help force you to modularize code, too.

I ended up making an uber-PreferencePane that controls all my stuff, and give a location to get it with my patches so that someone can test-drive how I think it could work.

Some Manual/Hidden Preferences

Some prefs.js settings. If you run Mozilla, you should be able to open about:config to see all kinds of neat settings. It's pretty much empty in Chimera. For a bunch of raw settings, look for .js files in your Navigator.app/Contents/MacOS/defaults/pref directory. Not sure what all works or anything, but there's quite a bit there. I stuffed one or two in my ~/Library/Application Support/Chimera/Profiles/ default/*.slt/prefs.js file and they seemed to work. i.e. I took:
pref("browser.blink_allowed", true);
from all.js and put it in prefs.js as:
user_pref("browser.blink_allowed", false);
Some examples (from all.js or playing with SpeedChimera)
  • user_pref("network.http.pipelining", true);
    - Turn on http pipelining, an experimental and very likely dangerous performance feature. Make sure you have autosave windows first ;)
  • user_pref("accessibility.tabfocus", 4);
    - Allow the Tab key to enumerate all items on a page.
  • user_pref("browser.blink_allowed", false);
    - Disable the HTML blink tag.
  • user_pref("image.animation_mode", "once");
    - Animated gifs run once and don't loop.

Setting up Search

  • Searching from the URL bar:
    • Do a test query on your favorite engine and make a bookmark.
    • Open the Sidebar and edit the bookmark (select it and hit Cmd-I) It will look something like:
      http://search.yahoo.com/bin/search?p=test
    • Change the query to "%s"
      http://search.yahoo.com/bin/search?p=%s
    • Set the keyword to ? (or any other thing you want, really)
    • Rename/file the bookmark as desired. I personally have a folder of keyword-set replacement bookmarks, since you only hit them via keywords, no need to pollute your bookmarks menu.
    • Then you can type "? food" into the URL bar, and it will do the replacement. Very handy.
    • On some sites, the query is done via POST instead of GET, so the arguments/variables are hidden from the URL. If you look at the source of the page, you can probably construct a GET URL that will work. For instance, http://us.imdb.com/Find?for=%s
  • Searching from the Search button:
    • In the Preferences->Navigation->Search Page
    • Unclick Use System Preferences
    • Set the Search Page to: (you might need to paste this as one line)
      javascript:q=prompt("Yahoo","");
      if(q){location.href='http://search.yahoo.com/bin/search\?p='+escape(q)}
      else if(q==null){location.href=''} else
      {location.href='http://www.yahoo.com/'}
      
      (Thanks to Jesse Widener on the Chimera mailing list for the idea and javascript)
    • Note that any question marks or ampersands need to be escaped with backslashes, and you should be able to add more URL text after the escape() with +'moreoptions' (though you can usually just rewrite the URL so the query is at the end :)

Other non- or semi-documented features

  • Delete and Shift-Delete are bound to Back and Forward.
  • command-[ and command-] are bound to Back and Forward.
  • command-left and command-right are bound to Back and Forward.
  • command-{ and command-} are bound to Next and Previous Tab
  • command-~ and command-` are bound to Next and Previous Window.

Supplying a patch

Creating a patch can be a little tricky. You need to provide your source code diffs, which is actually pretty easy. Changes to binary files (such as the menu or window .nib files, or the Localizable.strings) are trickier. Here's how I made one patch. You can get all of my patches from my patches page.

First, make the sourcecode patch, from mozilla/chimera, generally.

cvs diff -u12 > patchfile
The way to apply this patch later is:
patch -p0 < patchfile
If you need some binary files, tar them up, i.e.:
tar -zcvf patchextra.tgz \
	resources/localized/English.lproj/Localizable.strings \
	resources/localized/English.lproj/MainMenu.nib
I also make a patch.txt file, summarizing the changes, referencing appropriate Bugzilla BugIDs, and describing how to apply and use the changes, including preference settings as needed, and then bundle the patches, other files, and .txt file into a patch_bundle.tgz. Then I submit the .patch file and the patch_bundle.tgz file to the appropriate bug, and hope someone has time to look at it :)

Backing out a patch

If you apply a patch to your live tree and want to undo it (either to remove the feature, or to update it from a clean patch) without starting from scratch, and you have the old patch lying around still, you can do:
patch -R -p0 < patchfile

Distributing your own Chimera build

Well, this isn't really recommened, but in case you want to know how I do it... I use my own dmg_backup program to make Disk Images from the command line. Actually, I have a script that calls dmg_backup for me. It relies on several things, like a command line version of StuffIt in /usr/local/bin/stuff, a script called maketar to make the .tgz file you want, and some customizations in dmg_backup 1.3, that I may or may not release some day, laziness depending. Anyway, there it is, if you really want to see it.

Using Project Builder

(Needs work.)

Using Interface Builder

(Needs work.)

Cocoa programming resources

There's some great websites: There's some great books (programming and OS X in general) And there's a lot of documentation already on your computer (these links work in Chimera, might or might not work elsewhere) For some more info, see the readme from my Chimera Custom package, it has info on how I modified .nibs with Interface Builder and some other tidbits.

I also have some mockups from other people and the Chimera mailing list here

Feedback?

Content Enhanced - Use Any Browser

Home | Contact