Building a C++ XPCOM Component in Windows

February 7th, 2010

I’ve been teaching myself to write Firefox extensions for the last few weeks, and became interested in XPCOM components. Unfortunately, I couldn’t find a good (and recent) summary of them, and had to spend 3 or 4 days cobbling together various tutorials, so I figured it’s time to write one.

What is XPCOM?

XPCOM is a language-agnostic communication platform used in Mozilla products (and some other random pieces of software) to allow code (specifically extensions) to be written in a wide variety of languages.

Why would I want to use XPCOM?

There are two ways to “use” XPCOM. First, you can call functions through XPCOM. For example, the Firefox bookmarks service uses an XPCOM interface. So in order to interact with this service from Javascript you would do something like:

var bmarks = Components.classes["@mozilla.org/browser/bookmarks-service;1"].getService();
bmarks.QueryInterface(Components.interfaces.nsIBookmarksService);
bmarks.addBookmarkImmediately("http://www.mozilla.org","Mozilla",0,null);

There are plenty of tutorials on doing this as it is the more common use for XPCOM, so I won’t go into any detail on it here.

The second way is to write an XPCOM service. That is what this tutorial covers. Sometimes you need extra functionality, speed, or just want to tie into some library that requires a different language. Most commonly this is C++, but there is also JavaXPCOM and PyXPCOM (and probably a few others). I’ll be talking about C++, since it’s what I needed for my project.

Warnings

  1. Before you trudge through this: you most likely are in the wrong place. Firefox extensions are usually all Javascript. If you can use JS to do what you want, stop now. There is no need to go through the complexity of an XPCOM component when you can just use JS. Go read a tutorial about writing extensions and get to work.
  2. There is something called ctypes coming to FF 3.7 that may make doing this a lot easier. I haven’t touched this at all, but it may be worth considering if you can wait for the functionality and only need to tie into a particular DLL for some functionality. Long story short, XPCOM may become the more difficult way to call C++ functions from FF extensions.

My Setup

  • Windows 7
  • Visual C++ Express 2008 (free from Microsoft’s website)
  • Firefox 3.6 (Gecko 1.9.2)
  • A UUID or GUID generator. This is a unique (read: random) ID that identifies your app to the world. Windows and Linux have tools to generate this (guidgen & uuidgen, respectively), or you can find various online generators (Mozilla links to several). I recommend this one since it gives you the C++ encoded form too, which you will need. You need two different UUIDs: one for your interface and one for your component.
  • Ability to read and understand C++

Sample Code

If you don’t want to go through the tutorial and just want everything to work, then download this sample code. Just follow step #1 of the tutorial, then make sure your Gecko SDK directory is set right in the build step, and you can breeze on by most of this article.

The Tutorial

This is mostly paraphrased from Alex Sirota’s great tutorial, but it hasn’t been updated since 2005 and is a bit outdated. This new one should work out of the box for FF 3.6.

This tutorial will create a component called MyComponent with one function: Add, which will take 2 numbers and, surprisingly, return the sum.

  1. Download the Gecko SDK for your version of Firefox. I used 1.9.2 for FF 3.6.
  2. Create an idl file - IMyComponent.idl, with the following (replacing ***IUID*** with your interface UUID):
    #include "nsISupports.idl"
    
    [scriptable, uuid(***IUID***)]
    interface IMyComponent : nsISupports
    {
      long Add(in long a, in long b);
    };
    

    This file is a language-agnostic interface definition which you can read more about here.

  3. Generate the interface header and typelib files out of the interface definition file. Assuming you extracted the Gecko SDK to C:\xulrunner-sdk\, run the following commands (from the directory you saved IMyComponent.idl to):
    C:\xulrunner-sdk\sdk\bin\xpidl.exe -m header -I C:\xulrunner-sdk\idl .\IMyComponent.idl
    C:\xulrunner-sdk\sdk\bin\xpidl.exe -m typelib -I C:\xulrunner-sdk\idl .\IMyComponent.idl
    

    These will create IMyComponent.h and IMyComponent.xpt, respectively.

  4. IMyComponent.h has two snippits of code that you can use for the next two files. Everything between /* Header file */ and /* Implementation file */ can be used for MyComponent.h:
    1. Start by inserting double inclusion protection code and the right include:
      #ifndef _MY_COMPONENT_H_
      #define _MY_COMPONENT_H_
      
      #include "IMyComponent.h"
    2. Add the following lines, which define your component name, contract ID, and CUID (where ***CUID*** is the C++-style component UUID, of the form { 0×12345678, 0×9abc, 0xdef0, { 0×12, 0×34, 0×56, 0×78, 0×9a, 0xbc, 0xde, 0xf0 } }):
      #define MY_COMPONENT_CONTRACTID "@example.com/XPCOMSample/MyComponent;1"
      #define MY_COMPONENT_CLASSNAME "A Simple XPCOM Sample"
      #define MY_COMPONENT_CID ***CUID***
    3. Copy in the snippet from IMyComponent.h, replacing all the instances of _MYCLASS_ with the name of your component (MyComponent).
    4. Finish off the double inclusion protection code with #endif //_MY_COMPONENT_H_
  5. Everything between /* Implementation file */ and /* End of implementation class template. */ can be used for MyComponent.cpp:
    1. Start by inserting the right include:
      #include "MyComponent.h"
    2. Copy in the snippet from IMyComponent.h, replacing all the instances of _MYCLASS_ with the name of your component (MyComponent).
    3. Add some implementation to the Add method. I replaced return NS_ERROR_NOT_IMPLEMENTED; with
      	*_retval = a + b;
      return NS_OK;
  6. Create your module definitions files:
    #include "nsIGenericFactory.h"
    #include "MyComponent.h"
    
    NS_GENERIC_FACTORY_CONSTRUCTOR(MyComponent)
    
    static nsModuleComponentInfo components[] =
    {
        {
           MY_COMPONENT_CLASSNAME,
           MY_COMPONENT_CID,
           MY_COMPONENT_CONTRACTID,
           MyComponentConstructor,
        }
    };
    
    NS_IMPL_NSGETMODULE("MyComponentsModule", components) 

You now have all of the files needed to build an XPCOM component:

IMyComponent.h
IMyComponent.idl
IMyComponent.xpt
MyComponent.cpp
MyComponent.h
MyComponentModule.cpp

Now comes the hard part: getting the damn thing to build.

Building the code

Ok, it’s actually not hard since I’ve done most of the legwork for you. Assuming you’re using Visual C++ 2008 here are the settings you need (again assuming C:\xulrunner-sdk is where your Gecko SDK is). In Project->Properties:

Configuration Properties
  General
    Configuration Type: .dll
  C/C++
    General
      Additional Include Directories: C:\xulrunner-sdk\include
    Preprocessor
      Preprocessor Definitions: XP_WIN;XP_WIN32;XPCOM_GLUE_USE_NSPR
  Linker
    General
      Additional Library Directories: C:\xulrunner-sdk\lib
    Input
      Additional Dependencies: nspr4.lib xpcom.lib xpcomglue_s.lib

If you put the idl file into your project, be sure to mark it “Excluded from Build” in its properties…we don’t want VS touching it.

Cross your fingers, pray to whatever deity you believe in, and hit the build button. If it didn’t work let me know why in the comments and I’ll try to build a troubleshooting section.

Installing/Testing the Code

  1. Copy two files to C:\Program Files\Mozilla Firefox\components:
    • The DLL the build generated
    • IMyComponent.xpt
  2. Normally, if this was installed as part of an extension, it would automatically search this directory and find these files. But now we have to force a refresh. Delete xpti.dat. and compreg.dat from your profile directory (FF will regenerate them on next restart)
  3. Close Firefox and open it with this test file (MyComponentTest.html in the sample code):
    <html>
    <script type="text/javascript">
    function MyComponentTestGo() {
    	try {
    		// normally Firefox extensions implicitly have XPCOM privileges, but since this is a file we have to request it.
    		netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
    		const cid = "@example.com/XPCOMSample/MyComponent;1";
    		obj = Components.classes[cid].createInstance();
    		// bind the instance we just created to our interface
    		obj = obj.QueryInterface(Components.interfaces.IMyComponent);
    	} catch (err) {
    		alert(err);
    		return;
    	}
    	var res = obj.Add(3, 4);
    	alert('Performing 3+4. Returned ' + res + '.');
    }
    </script>
    <body>
    <button onclick="MyComponentTestGo();">Go</button>
    </body>
    </html>
  4. One last time: cross your fingers, pray to whatever deity you believe in, and hit the Go button. If it didn’t work let me know why in the comments and I’ll try to build a troubleshooting section.

Hopefully this clears up what looked like a lot of confusion to me. I will keep this updated to the best of my abilities and hopefully it will continue to be useful for a long time.

, ,

Yet Another Reason Not to Use GoDaddy

August 9th, 2009

Update: It seems GoDaddy fixed this issue. Glad they fixed it, though I still can’t say I recommend them. Thanks Sean for pointing that out to me.

I recently gave a talk on WordPress plugins at the Boston WordPress Meetup (slides are online on their meetup.com page). During this talk the question came up of which webhost to use. Everyone has their own recommendation of webhost, but the one host that was universally panned is GoDaddy.

I’ve long since moved on from shared webhosts on to VPSes (I’ve been using VPSLink since they started. Use this link to get 10% off for life and snag me a nice referral bonus ;-) ), but GoDaddy was my first web host, and since then I have heard nothing but terrible things about their marketing practices, upsells, and other sketchiness. But they will show you videos of women taking their clothes off on their website, which is great if you’re into that sort of stuff, but not what I need from a webhost.

But until recently I never discovered anything actually wrong with their hosting service…it worked, and while it was cheap, they at least delivered what you paid for (though granted, you’re not paying or getting a lot). Yesterday, while looking through some code for a GoDaddy site, I discovered something painfully bad with their service that makes them a terrible idea for any ecommerce site.

It seems GoDaddy doesn’t allow outgoing connections from their shared hosting packages. So, for example, you can’t connect to authorize.net or paypal.com from your server. How do they recommend you accept payments? Send it through some unsecured proxy they have.

That’s right, GoDaddy is actually telling people to send credit card information to another server in unsecured plaintext. They then forward it along and send you the response. There are 2 things wrong with this.

  1. Man in the middle attacks up the wazoo. Not only can someone possible get in between the proxy and your server, but who knows whether or not the proxy requires valid certificates. If not then the entire workflow is vulnerable.
  2. There’s a now a single point of failure for all of that hosting. If that server is compromised, all those sites get compromised too. This is a huge risk.

If I were issuing merchant accounts and knew about this, I wouldn’t accept accounts from anyone hosted with GoDaddy. There are very few reasons for GoDaddy to be doing this. The only one I can think of is preventing the spread of website worms that use holes in website scripts. This is a pretty lame reason to lock websites into their own box, and there are much better ways to stop this.

Speaking at Boston WordPress Meetup Tonight

July 27th, 2009

Totally forgot to post this, but I’ll be the speaker at tonight’s (Monday July 27) Boston WordPress Meetup at Microsoft’s New England R&D Center (1 Memorial Dr in Cambridge). If you’re interested in hearing about WordPress plugins, or WordPress in general, feel free to show up. Should be a blast!

I’ll post my slide deck after the presentation.

On Spriting and Website Build Processes

June 3rd, 2009

Ryan, my mentor from when I was at MoCo, wrote an excellent article on spriting a few days ago. Man, I miss all the good conversations…that’s what I get for not subscribing to the webdev feed. That’s all changed now.

I have an interesting history with spriting. For both of my internships (Mozilla and Yahoo), one of my first projects was to sprite sites (AMO & Yahoo! Real Estate, respectively). AMO is still using my work (for now), and YRE long ago gave up on rounded corners, and have few icons now, so spriting is more or less useless for them now.

I guess that qualifies me to weigh in a bit. I’m not going to talk about the memory usage or anything on the client-side past delivery for two reasons. First: size in RAM has a tiny impact on page-loads compared to downloading for most users. Second: I’m horrible unqualified to talk about how web browsers load and store images in ram. Not my field.

There are some issues (many fixable) with spriting:

  • In both instances of spriting I got an email a few months after I left the company asking me where my source sprite file was located. Once it was due to me not putting the file in the right place, but the fact still remains that a source sprite file is one more thing to lose, and losing it is pretty annoying.
  • Not everyone knows how to compress images properly. If you do it wrong, you’ll end up with a huge PNG file that is worse than a bunch of small files.
  • Spriting removes any connection between CSS styles and the images they are associated with. If I want to know what a class with a background image looks like, I have to either find a reference to it on the site, or figure out where the hell -123px -72px is. It’s more or less obfuscating your CSS.
  • repeat-x or repeat-y images need to span the whole width/height of the sprite…they should have their own sprite files (if you have enough of them)
  • Like Ryan said, images that are supposed to be near the left of an element (i.e. they need an arbitrary amount of space to the right of them) should be at the right side of the sprite. This makes an internationalized site that supports right-to-left text (an insanely difficult thing to achieve, and something which many MoCo sites do impressively well) much more difficult. It’s very frustrating to discover halfway through that your sprites show up wrong in RTL. Not many sites need to worry about this, but it’s annoying if you do.

With that being said, spriting can be useful when done properly. A very good example is how Yahoo Mail sprites. They group similarly-size things in a file. They have an icon file, a rounded corners file, etc. This makes things easier to manage, but still suffers from some of the above issues.

I don’t know how Yahoo’s build process works, but to make sprites worth it I would propose some kind of CSS build step. Ideally all the developer would have to do is specify which images are displayed which ways (whether there needs to be whitespace to the left, right, top, or bottom), and the system will build the sprite and generate a complied CSS file that is minimized and concatenated.

AMO’s build process got halfway there. It was originally a shell script written by yours truly, converted to python by FWenzel. The shell script is described here (the python script is essentially the same). It concatenates a bunch of files, compresses them with YUI Compressor, and creates a PHP file with the current revision numbers (to append to the URL for long expires headers). Pretty standard web build system.

I’d really love to see a system that also parses out background*: rows with comments at the end (identifying what type of spriting should be done), places them, replaces the URL, and adds a background-position. That, combined with AMO’s build process, would allow an algorithm to determine what’s best for download times vs. memory usage.

The best part: an automated system would easily allow bucket tests on load times based on different sprite files. Now wouldn’t that be useful?

Why YouTube Can’t Cost $1.65M a Day

May 29th, 2009

I know I’m a little late on the bandwagon, but I’m sick of seeing articles claiming Google is losing so much money from YouTube. They’re based on estimates that I find a little absurd.

Let’s take just one example - http://www.internetevolution.com/author.asp?section_id=715&doc_id=175123&:

Disclaimer: I’m going to use low-ball estimates to guess where Google stands. I am by no means a professional when it comes to estimating this, or even educated in such things, but I feel like Google has so many tricks up their sleeves that I have only slightly less credibility than Credit Suisse or Bear Stearns. I repeat: I am making up numbers

Bandwidth

Maybe I’m not understanding something here, but from what I know a mutually beneficial peering agreement doesn’t require paying for anything other than labor or hardware. ISPs don’t want to provide users with fast internet without having it bounce around too many places, as does Google. Assuming Google has a datacenter close enough to every major ISP to just peer to them (not unreasonable), their only potential non-labor cost is communication between their datacenters. But wait! Hasn’t Google been buying up dark fiber left and right? That means their costs are adding additional capacity and redundancy, plus network maintenance (hardware and labor). Like I said, I’m not a hardware guy, but knowing people who owned datacenters, this is how I understood it. Please call me stupid if I’m wrong, but I’m going to cut this down to $10,000 (~$3.5M/year).

Revenue Share

This one’s just BS…they’re counting “making less money” as an expense. Sure, it’s less money, but that’s a little misrepresenting, no? Plus, I assume the estimating companies already took this into account. $0

Content Acquisition

Google isn’t dump…I doubt they’re putting themselves in the red solely on content costs. Media companies also get a huge amount of exposure from being on YouTube that they can’t get anywhere else (Excluding TV shows and Hulu, but YouTube mostly deals in music videos anyway). So let’s cut this in half and say $360,000 (~$131/year, a lot of clams, and generous in my opinion!).

Hardware

“Given market estimates of about $2 per gigabyte”. Really? Google is famous for using off-the-shelf hardware. I can buy a 1TB HDD for $100, and I’m not buying a million of them. I’m not saying this quote isn’t accurate when you account for electricity, cooling, redundancy, etc, but Google is far above the average for all of these, so I feel like using a market estimate is unfair. I’m gonna cut this in half, so $18,000 (~$6.5M/year).

New Results

Let’s calculate Google’s break-even point for YouTube:

Bandwidth $10,000
Content Acquisition $360,000
Revenue Share $0
Hardware $18,000
Subtotal $388,000
Administrative Costs 38.4%
Math! x*(1-.384)-388000=0
Break-even Revenue x=$629,870

Now I’m not saying that Google is definitely making money off of YouTube, but $630K/day is less than Credit Suisse estimates Google’s daily revenue at, so it’s entirely possible they’re in the black.