Morning Coffee, blogs, and XPCOM

The best part about this stage of the term in our Seneca Mozilla and Open Source development courses is that the students are really digging into some interesting work, and it makes my morning coffee and blog-reading an incredibly satisfying experience.  Here's some of what I woke-up to today, and it's inspired me (I need a lot of inspiration these days):

There's one more student blog I read this morning, and I was in the process of leaving a comment when I realized I should probably blog about it instead.  The question is related to things we're studying this week and next (i.e., XPCOM), so let me deal with it here.

Michael Dennis is waiting on a review of a Thunderbird bug he fixed for his 0.1 release, and instead of sitting there making excuses and wasting time, he's decided to jump into a second bug (fantastic!).  His first bug was a UI issue that had him working with JavaScript, CSS, and XUL.  His second bug is down in the bowls of the C++ code.  He writes:

So, here comes my new bug, 286760, which was given to me last week. Basically, this bug is about mail addresses that are saved in the address book ending with a space cause problems later on when they are used. From this, I figured it wouldn't be so hard to fix the error, because how much implementation code do you need to concatenate a name and an email address into, for example, the following:

_ex: Mikey <>_
_ _
Well, I definitely got surprised once I saw the [code]( to actually do this. All these OOP344 flashbacks came back to me.. * cries *. From pointers, to memory allocations, just ugh... I am definitely not a fan of C++. Keep in mind, I am still not even sure if this is the right place. Starting from an [interface](, I searched for the [makeFullAddress(...)]( and chose the [only match]( that had an implementation for it; however, the uncertainty is because the makeFullAddress(...) should only accept two parameters, yet the implementation shows that is accepting three parameters, which makes me ask myself if I am going in the wrong direction.
Anyways, let's say I am on the right track. Exploring the makeFullAddress(...), I ended up at [msg_make_full_address](, which contains code that seems helpful. However, there is one problem: new keywords are popping everywhere, for example, PRUint32 and PR_Malloc(...). To overcome this, I assume what it is or what it does, but how far can I go by doing this...

Heh, feel free to offer me suggestions.
I have a few suggestions.  First of all, you're in the right place.  In Mozilla, all (there are a few exceptions, but...) XPCOM methods return a numeric result code to indicate success or some failure.  You'll see lots of references to things like NS_OK (it worked!) and NS_ERROR or NS_ERROR_NULL_POINTER or a host of other specific error results to tell you that it failed and why.  These are simply 32-bit integer values that are defined in the code using macros.

The reason for all these result codes is that Mozilla doesn't use C++ exceptions (again, there are a few 'exceptions', but...) for historical/compiler reasons.  This means that every method you write has to return an integer value indicating success/fail, and a lot of the C++ code is devoted to checking those values and responding accordingly, much of it hidden inside convenience macros.

What does this mean for the case where you want to have a method return a value?  If you're forced to return an integer result code, how do you also return a string or object?  The answer is that you add a third parameter to take the result.  Let's look at the IDL signature for the method Michael is calling (IDL is the interface language Mozilla uses to declare its XPCOM classes and members):

  • Given an e-mail address and a person's name, cons them together into a
  • single string, doing all the necessary quoting.
  • @param aName The name of the sender.
  • @param aAddress The address of the sender.
  • @return A string of the form name <address>.
    AString makeFullAddress(in AString aName, in AString aAddress);`

It even has comments!  Notice how you have a method that takes 2 arguments (Mozilla's abstract string type), and returns a third.  No mention of a result code.  Now, when you build Firefox or Thunderbird, the xpidl compiler will take this IDL declaration and translate it into C++, creating a header file that other parts of the C++ code include.  Here's what that looks like:

NS_IMETHOD nsMsgHeaderParser::MakeFullAddress(const nsAString &amp;aName, const nsAString &amp;aAddress, nsAString &amp;aResult)

Here the nsMsgHeaderParser is implementing the nsIMsgHeaderParser interface (notice the 'I'), which includes the makeFullAddress method.  When it turns into C++ the name changes to MakeFullAddress (in JavaScript it would still be makeFullAddress, see below), and the signature has been rewritten to include a new third argument, aResult.  Also, the return type is changed to nsresult (which is what the NS_IMETHOD macro does, and nsresult is just a typedef of a PRUint32 which is just a cross-platform compliant unsigned 32-bit integer).

Notice how the first two arguments are const, and the third one is not.  That's because the third argument is going to be modified, and a resulting string is going to get "returned" via this argument.  This makes more sense when you see it being called:

parser-&gt;MakeFullAddress(pDisplayName, newRecipient.mEmail, newRecipient.mAddress); if (newRecipient.mAddress.IsEmpty())

Here you see 2 strings being passed, and a third string being given for the return value.  This example is somewhat opaque in terms of the use of the result code, since the caller is ignoring it, instead choosing to look at the length of the third argument after the call has returned.  It comes to the same thing.  Another common way to write this would have been:

nsresult rv; rv = parser-&gt;MakeFullAddress(pDisplayName, newRecipient.mEmail, newRecipient.mAddress); if (NS_ERROR(rv))

Just to complicate things, this same call can be made from JavaScript, and looks different again:

var address = gHeaderParser.makeFullAddress(card.displayName, card.primaryEmail);

Here there are only 2 arguments, and notice how the resulting string is once again returned from the method, just as the IDL specified.  That's right, you have to understand that it works differently in C++ and JavaScript, but is the same method either way.  It's not as hard as it seems, but it's hard enough to cause new developers to stumble.  Being able to declare, define, and call code across language boundaries is powerful and complicated.

All of this to say, I can understand why Michael is confused about whether or not he's looking at the right code.  The answer is 'yes' but it takes some understanding of how our code is structured to see why.  If you didn't get all this, don't worry.  There will be more XPCOM lore in class this week and next, and lab to help you get more experience with it.

Show Comments