Page tree
Skip to end of metadata
Go to start of metadata

Introduction

Life-cycle of a plugin is a little bit different, and we've done the best we can with FireBreath to keep that clean; this page is intended to clarify the order that things get called in.

Terminology

In this (and nearly every) document, we use terms that may not be immediately clear. Here is a quick glossary of commonly misunderstood terms:

  • Plugin Object - This is the object in your project that inherits from PluginCore. It is the primary owner of the plugin resources.
    • This class has the same name as your plugin (e.g. SomePluginName.h and SomePluginName.cpp)
  • JSAPI Object - This is a class that extends JSAPI or class FB JSAPIAuto in order to provide an API that can be called from JavaScript. A plugin always has a Root JSAPI Object. Whenever JavaScript calls methods or properties on the plugin, it will go through the Root JSAPI Object, e.g. plugin().method1() will call method1() on the Root JSAPI object. You can return a JSAPI instance to a JavaScript caller in the form of a JSAPIPtr and use that to make calls on a different JSAPI object, e.g. plugin().method1().method2() will call method1() on the Root JSAPI object and method2() on the JSAPI object returned in the first call.
  • BrowserHostPtr - a BrowserHost shared_ptr which can be used to interact with the browser. Contains methods such as getDOMWindow and getDOMDocument. There is one BrowserHost object per plugin instance.

Concurrency issues

The first thing to note is that plugins run in a web page, and they run in a browser process. The browser keeps the plugin process around for as long as any plugin instance is running or far longer. You have no control over this. This means that:

  • Global and static variables may never be reset and are shared among all plugin instances
  • Process state variables like the "Current Working Directory" and such could change without any notice!

As a general rule of thumb, do not use global or static variables unless you are 100% certain you know what you are doing!

On top of that, at no time are you guaranteed that a plugin will be completely shut down before the next one (in the case of a page refresh or changing pages) is created; in fact, often the next one is created and then the previous shut down. Consider the following possible situations:

  • You refresh the page
    • Likely, but not guaranteed, in this case the next plugin will be instantiated before the original instance is destroyed
  • You open three browser windows or tabs and have the plugin page open in each
    • You will have three instances of your plugin running at the same time.
  • You have three different browsers (Internet Explorer, Firefox, and Chrome) that all have your plugin open at the same time
    • Different browsers means different processes, so there is no interaction between the three, but keep in mind that using shared resources, e.g. writing to the same file, still has to be taken care of.

Global Initialization

Sometimes it is necessary to initialize something only once for the entire plugin process; singleton classes that are shared by every instance, for example. When the plugin process is created, before your constructor is called the global function globalPluginInitialize() is run. The function is implemented in Factory.cpp and calls staticInitialize() on your plugin object. 

When the plugin process is destroyed, after your destructor is called, the global function globalPluginDeinitialize() will be called. Correspondingly, this function in your Factory calls staticDeinitialize() on your plugin object.

Plugin Startup

Order of calls during plugin startup:

  1. Plugin Object constructor
  2. Plugin FSPath (Filesystem path to the DLL on windows) is set, if on windows
  3. BrowserHost object is set on the Plugin Object (inherited from PluginCore)
  4. The browser gives the plugin a window (platform dependent) and the Plugin Object receives AttachedEvent.
  5. FireBreath requests the root JSAPI object from the Plugin Object by calling getRootJSAPI(). getRootJSAPI() calls createJSAPI(), which is a function in your plugin class, to instantiate the root JSAPI object if it hasn't been instantiated already.

At this point your plugin should be completely initialized and ready to go. Please keep in mind that since the plugin constructor is called first, anything you do in that constructor must not require a BrowserHost or a window, since those are not initialized until later. For these operations wait for onPluginReady() to be called.

Plugin Shutdown

Order of calls during plugin shutdown:

  1. Plugin Object receives DetachedEvent – immediately after this the plugin window will be destroyed, so act accordingly
  2. shutdown() is called on the Plugin Object.
  3. Plugin Object destructor is called.

NOTE: JSAPI objects are owned by the browser, and may or may not be destroyed before the Plugin Object. If the Plugin Object retains a reference to the JSAPI object, it will not be cleaned up until after that reference is released (such as the root JSAPI object which the Plugin Object keeps a reference to automatically).

It is usually a safe bet to act as though your JSAPI objects will stay around longer than the plugin object, so any pointers you give them to resources controlled by the plugin must be cleared when the plugin goes away so that the API object doesn't keep trying to access the Plugin Object after it has been freed.

  • No labels