FireBreath Tips: Dealing with JSAPI objects

November 9, 2010 4 Comments by Richard

FireBreath Tips and Tricks

I have decided to start writing some tutorial type information for solving specific problems using FireBreath.  I don’t know how often I will post on this topic, but my goal is to do a series of short “tips and tricks” posts whenever I think of something that may not be obvious to someone starting out with the framework.

This post will discuss some best practices for using JSAPI objects. Feel free to request future articles in the comments.

boost::shared_ptr

The FB::JSAPIPtr type in FireBreath is just a convenience alias — mostly because I’m lazy — for boost::shared_ptr<FB::JSAPI>.  This is very important to understand, because if you try to use a normal pointer to your JSAPI derived objects without using shared_ptr, you’re likely to have your object disappear out from under you.

How it works

Basically when you create a boost::shared_ptr object, it allocates a small structure for holding two reference counts.  The first and most important is the use_count.  The second is the weak_count, and we’ll discuss that later.  The important thing to know is that every time you assign your shared_ptr to another, it increments the use_count.  Whenever a shared_ptr gets destroyed / goes out of scope, it decrements it.  Once that count hits 0, the object inside the shared_ptr goes away.

This is critical in FireBreath for JSAPI objects because we don’t know when the Browser will let go of our objects, and we can’t be deleting them while the browser still has a reference.

Creating objects

The best way to create a new object is to put it directly into a JSAPIPtr (or other shared_ptr).  You can (and should) also use boost::make_shared to be explicit. For example:

FB::JSAPIPtr myAPI(boost::make_shared<FBTestPluginAPI>());

This creates a new FBTestPluginAPI object and stuffs it into the myAPI JSAPIPtr.  Now, usually you’ll be passing something into the constructor, so it’ll look more like this:

FB::JSAPIPtr myAPI(boost::make_shared<FBTestPluginAPI>(FB::ptr_cast<FBTestPlugin>(shared_ptr()), m_host));

Of course, you may want a shared_ptr that allows you better access to your object.  If so, you can also do this:

boost::shared_ptr<FBTestPluginAPI> myAPI(boost::make_shared<FBTestPluginAPI>(FB::ptr_cast<FBTestPlugin>(shared_ptr()), m_host));

You can return this in a function that returns FB::JSAPIPtr and it will cast up-cast it implicitly just like a normal pointer.

There are a few unexpected things here:

  • shared_ptr() - return a boost::shared_ptr<PluginEventSink> object, which is the ultimate base class for all Plugin objects (PluginCore derives from it).
  • FB::ptr_cast<FBTestPlugin>(shared_ptr()) – FB::ptr_cast does a dynamic cast of a shared_ptr. This is basically an alias for boost::dynamic_ptr_cast, which is more of a pain to type.
  • m_host – this is the BrowserHost object, which is useful to have around.  Think of it as a dependency injection container that gives you access to the browser functions and tools.

An important thing to realize here is that we aren’t storing the shared_ptr to the plugin object inside the JSAPI class. Instead, we’ll use a boost::weak_ptr.  A weak_ptr uses the other reference count, and the reference counting structure is not destroyed until both the use_count and weak_count hit 0.  To use the plugin from a weak_ptr, you use the .lock() method on the weak_ptr to get the shared_ptr for that class, which prevents it from being destroyed while you’re holding it. However, when you’re not using the plugin, it can be destroyed when the time comes.

This is important because the Plugin object usually has a reference to the API object, and we really don’t want circular references =]

Best practices state that you should always check to see if .lock() returns a NULL pointer indicating that the plugin has gone away.  We recommend a getPlugin() method on your API object like this:

boost::shared_ptr<FBTestPlugin> FBTestPluginAPI::getPlugin()
{
    boost::shared_ptr<FBTestPlugin> plugin = m_pluginWeak.lock();
    if (!plugin)
        throw FB::script_error("The plugin object has been destroyed");
    return plugin;
}

When you need to use it, simply call getPlugin() and make sure you let go of the resulting shared_ptr when you’re done.

Storing JSAPI objects

If you want to store an array of JSAPI objects, that’s just fine… but make sure you store them in an array of boost::shared_ptrs or boost::weak_ptrs depending on whether you want storing them to keep them around as long as your list is around.

Casting JSAPI objects

If you have a FB::JSAPIPtr and you want to cast it to your plugin API type, you can use either boost::dynamic_pointer_cast or FB::ptr_cast. For example:

boost::shared_ptr<FBTestPluginAPI> api( FB::ptr_cast<FBTestPluginAPI>(m_api) );

That’s it!

That’s it! The #1 thing to remember is to NEVER EVER EVER store or pass around a JSAPI object as a raw pointer.  That’ll get you in trouble =]