Misadventures writing free software

Writing software, the wrong way

Wednesday, April 16, 2008

Nearing the final release

I got disabling updates working for Firefox, though it is terrible hack since I couldn't find a policy to disable IE updates. A google search didn't help, and most of them pointed to third party tools to disable updating to IE7 from Windows Update. So I had to manually create a registry key to disable browser updates (ironically enough, I unconsciously put it in the Internet Explorer branch of the registry).

There are still so many things to do, most of it cosmetic. For example, the code is still a mess. I have my doubts about fixing it entirely, but I can certainly comment.

So here is what is left to do, at minimum, where I stand :
  1. Document on the wiki how to implement a new policy
  2. Use javascript components whenever possible
And then I can finally say that I am done, and anything else is just frosting :)

Sunday, April 6, 2008

Overcomplicated

While doing some test cases for the javascript wrapper around registry keys, I found a major flaw in an assumption I was making about keys that made me rethink my code. While I made some things simplier, it was still a pain to get something. Here is a progression of how things used to be and how things are right now.

Declare the service :
let service = Cc["@shacknet.nu/winregistry;1"]
.getService(Ci.nsIWindowsRegistryService);


Oldest way :
let key = service.getKey(
Ci.nsIWindowsRegistryService.ROOT_KEY_LOCAL_MACHINE,
"Software\\Mozilla\\Firefox\\Crash Reporter\\EmailMe");
let answer = key.getValue();
key.close();


Older way :
let key = service.getKey(
"Software\\Mozilla\\Firefox\\Crash Reporter",
"EmailMe");
let answer = key.getValue();
key.close();


New and simplier :
let answer = service.getKey(
"Software\\Mozilla\\Firefox\\Crash Reporter",
"EmailMe");


I figure, this will work for 100% of the use cases right now. I was gonna get complicated with making another, more complete, interface for getting attributes of keys (ie. what was the root, path, and key name). But this early in the process, especially without requirements, I don't care about it :)

Thursday, April 3, 2008

Mystery unravelled!

A bit of my last release failed. Mainly because my javascript component was failing in an odd place. I couldn't figure out why, and so xpcshell tests never got complete.

I went back to the problem today. Firing up venkman and the javascript shell, I started to play around with it some more. I manually tested (grumble) both components independently and they both worked. But they didn't work together. I couldn't get one to return an instance of the other, and it was bothering me.

Walking through the javascript in venkman, It got all the way to the return value and throws the exception there.

if (regkey.hasValue(value) == false)
return null;

key.init(regkey, relPath, value);

return key; /* Always failing here */
},

Looking at the terminal, I noticed a strange assertion coming up after the code fails. I asked in #developers, and even sdwilsh (a very knowledgeable and promising character that interned last year) took a stab at it and couldn't find anything obvious. I started losing hope of ever getting this blasted thing working. Looking up the assertion didn't help, and neither did a NS_ENSURE_TRUE warning that were both being fired after the code failing.

I finally had it. It couldn't be the .js file. It had to be something more. But what else is there except the idl interface? I looked, examining each line. Then I noticed something very, very, wrong.

void getKey(in unsigned long root, in string path);

The return value was void. I must have just put void there first because I didn't know what I wanted to call the other interface. I promptly change it to :

nsIWindowsRegistryKey getKey(in unsigned long root, in string path);

And it starts to work in the javascript shell, which is absolutely a relief. Now I can start seeing if tests work.

Tuesday, April 1, 2008

Dynamic Javascript

For the next release, I want to start cleaning up the code base. I've started with grouppolicy's browser.xul (which overlay's Firefox's browser.xul) because this is a file that confuses me quite a bit. I also found out why it's seemingly so confusing.

The problem is this: Say I have a function called multiply that lives in another file :
function multiply(a,b) {
...
}

Without changing multiply's function internals, how do I change the logic? You might consider making a multiply2(a,b) and copy and pasting code and substituting your own logic, but the catch is you can't change any of the functions that call multiply() either!

It's sorta the same dilemma that we have. There are functions in Firefox's browser.xul that we don't want to change, but we want to add bits of logic jammed into the middle that would, for example, prevent a user from ctrl+click a link to open it in a new tab when tabs are disabled via a policy.

Overlays offer one solution. You can declare your own multiply(a,b) function and it would overwrite the one in Firefox's. However, I don't like this approach because you have to copy and paste code. And when you do that, you're responsible for keeping it current.

Another solution to wrap around the function. Let's expand our multiply function :
function multiply(a, b) {
var rv = 0, i;
// If we go over 999, just give up silently because we're evil
if (b < 1000) {
for (i = 0; i < b; ++i) {
rv += a;
}
}
return rv;
}

And let's say you wanted to change 1000 to 500. One solution is to wrap the function. Something like :
var _multiply = multiply;
function multiply(a, b) {
if (b < 500) return _multiply(a,b);
return 0;
}

Now the real multiply doesn't get called unless the second factor is less than 500. This is the approach I'm using to solve my problem. However, there is sometimes duplicate code that you just can't get around.

A third option, which is quite innovative, is something I found someone doing on an addon. It uses eval(), which is risky but gets the job done in this situation.
eval(multiply.toString().replace("1000", "500"));
I'll leave that for you to figure out. But it works!

Yay, bedtime!

Monday, March 31, 2008

Windows Build Prerequisites change

If your building from source on Windows, then it will come as the most un-welcomed surprise (well, I have been told it's been there for a while) that you now need the Window's Vista SDK to do a build. The exact reason why escapes me at the moment. Something about terrorists winning if we don't.

Sunday, March 30, 2008

Makefile.in Madness

I tend to really hate doing Makefile.in files because they are just copied from somewhere and they sometimes don't have everything you need. And when you don't know what your doing, that can screw you up. I created a generic Makefile.in file that is usable for extensions, idl interfaces, and C++/javascript components. It's also documented as much as I could find information on it. Just uncomment what you don't need and delete the rest. I covered as much as I could, I might not have covered every single option, but I think I got most of them.

You can also use Ted Mielczarek's Javascript Component Wizard. I dislike using wizards, but to each his own.

If you want to repay me, you can add me to the contributor's list (Cesar Oliveira) for the Makefile.in.

Monday, March 24, 2008

Building OpenKomodo

I use OpenKomodo on windows all the time. It is a nice IDE for javascript and detects formats that you use in Mozilla development like .idl and .xul. The only bad thing about it is that it has a lot of Mozilla 1.8 (Firefox 2) memory problems. You can go over 700 megabytes just by doing nothing.

I wanted to have it here on my Linux box, but they do not offer a 64bit version of OpenKomodo. But because they released it late last year, I can build it. The experience was a little... refreshing. Reminded me of building Firefox for the first time.

If you can build Firefox, you should be able to build Komodo. The steps to build are outlined on their svn's README.txt. I'll have to hand it to them for getting something out in 3 months :)

There are a few things that I stumbled on when doing this that I will note here :

  • There build searches for your distro. I got an error :
    platinfo.InternalError: Could not determine distro & release from first line of
    '/etc/bluewhite64-version' (Bluewhite64 12.0.0).

    I had to add "Bluewhite64": re.compile("^Bluewhite64 ([\d\.]+)"), to line 583 of util/platinfo.py and later src/release/codeintel/support/platinfo.py.

  • I had to build python because they only offered the x86 binary. Two mistakes I made were trying to have it find python on my system and symbolic linking python. At one point during the install, something was taking a lot of time and disk was spinning like crazy. I stopped and looked and it was copying everything in /usr to some other directory. A time consuming process indeed.
    To solve this, I :

    1. got python 2.5 source
    2. created a folder in my home directory called root: mkdir $HOME/root
    3. untarred and configured python with --prefix $HOME/root and --enable-shared
    4. compiled. Then I went to $HOME/root and zipped up the contents : zip -r linux-x86_64.zip .
    5. Copied linux-x86_64.zip to mozilla/prebuilt/python-2.5/


  • Undefined reference to X...., was a already patched but not committed bug.

  • There is something in linux-x86.zip python binary that isn't in the linux-x86_64.zip binary we created. Had to unzip lib/python2.5/site-packages/activestate.py from linux-x86.zip and copy it to build/cvs1.8-ko4.3/mozilla/ko-rel-gtk2-ns-shared-tools/dist/python/lib/python2.5/site-packages/

  • I had to explicitly say the version : bk configure -V 4.3.0-devel

I hope this is useful for anyone else who wants to build it.