Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 4.0
Table of Contents

What is a JSAPI Method?

A JavaScript API Method is exactly what it sounds like -- a method on your JSAPI object that can be called from JavaScript.

A JSAPI Method definition consists of two parts:

  • A method on your JSAPI class
  • A registration for that method in the constructor of your JSAPI class

Creating an empty JSAPI object

Let's create a really really basic JSAPI class. We're going to use FB::JSAPIAuto as our base class, and we strongly recommend that you do the same!

Code Block
// MyPluginAPI.h
#include "JSAPIAuto.h"

FB_FWD_POINTER
class MyPluginAPI : public FB::JSAPIAuto
{
public:
    MyPluginAPI();
    ~MyPluginAPI() {};
};
Code Block
// MyPluginAPI.cpp
#include "MyPluginAPI.h"

MyPluginAPI::MyPluginAPI() : FB::JSAPIAuto("MyPluginAPI object") {
}

There! That was easy, wasn't it? If you pass an instance of this class back to the page, it will have two members: valid and ToString. These members are defined and registered by FB::JSAPIAuto, the base class.

Adding a New Method

To add a new method to our MyPluginAPI JSAPI interface, first we need to add the method to our class. Let's add some basic math functions, shall we?

Code Block
// MyPluginAPI.h
class MyPluginAPI : public FB::JSAPIAuto
{
public:
    MyPluginAPI();
    ~MyPluginAPI() {};

    int add(int a, int b);
    int subtract(int a, int b);
    int multiply(int a, int b);
    int divide(int a, int b);
};
Code Block
// MyPluginAPI.cpp

int MyPluginAPI::add(int a, int b) {
    return a + b;
}

int MyPluginAPI::subtract(int a, int b) {
    return a - b;
}

int MyPluginAPI::multiply(int a, int b) {
    return a * b;
}

int MyPluginAPI::divide(int a, int b) {
    return a / b;
}

Great! Now we have four methods in our JSAPI class, but we still can't use them from JavaScript. Remember part two of any JSAPI method: Registration. This determines what the name will be from javascript; as a convention, we name our method the same name we want to use to call it from JavaScript. To register the method, we add a call for each method in the constructor:

Code Block
// MyPluginAPI.cpp

MyPluginAPI::MyPluginAPI() : FB::JSAPIAuto("MyPluginAPI object") {
    registerMethod("add",  make_method(this, &MyPluginAPI::add));
    registerMethod("subtract",  make_method(this, &MyPluginAPI::subtract));
    registerMethod("multiply",  make_method(this, &MyPluginAPI::multiply));
    registerMethod("divide",  make_method(this, &MyPluginAPI::divide));
}

That's all there is to it! This object now has four new functions for doing math! If this were your Root JSAPI object, we could then access it from JavaScript like this:

Code Block
var plugin = document.getElementById("plugin");
plugin.add(3,5); // 8
plugin.multiply(3,5); // 15
plugin.divide(15,3); // 5
plugin.subtract(3,5); // -2

Strong dynamic typing

What would happen with the above example if we called it using strings instead of ints? Remember that javascript is a dynamically typed language, but C++ is not! So let's say we called it like this:

Code Block
plugin.add("15", 5);

What do you think the result will be? If you're a C++ programmer, it may surprise you to know that this call will actually return the integer value 20, just like it would if you'd passed it two integer values. Why? JSAPIAuto "Auto"matically converts the input values into the type expected by your method. Since your method expects two int arguments, JSAPIAuto will convert those arguments to int from whatever type they are – if possible.

Code Block
plugin.divide("30", "five");

If you were to run this for this function, it will throw a javascript exception; JSAPIAuto is pretty smart, but it can't convert "five" into 5.

By the same logic, though, if you accept parameters of type const std::string&, JSAPIAuto will convert any numeric types to their string equivalent and pass that value into your function. Whatever your return type, JSAPIAuto will pass that back to JavaScript in the closest representation that it can.

Note that JSAPIAuto does not automatically accept all types, but only certain "recognized" types; other types can be forced through by using the FB::variant class as a return type, but it usually isn't a good idea since the browser won't know what to do with it.

It is often a good idea to use const parameters and accept complex objects (such as std::string or FB::JSObjectPtr) by reference.

Optional parameters

Starting in FireBreath 1.4, you can specify optional parameters using boost::optional. For example:

Code Block
void MyPluginAPI::doSomething(int a, boost::optional<int> b, boost::optional<bool> c) {
...
}

you can then call it any of the following ways:

Code Block
plugin.doSomething(5); // b and c not specified
plugin.doSomething(5, 3); // c not specified
plugin.doSomething(3, 5, true); // all specified
plugin.doSomething(5, null, 3); // b not specified

However, if you were to omit the first parameter, an exception would be thrown.

JavaScript methods

If you want to pass in a JavaScript method as a callback, you can do that as well. The datatype you need is FB::JSObjectPtr. Note that all JSAPI-derived objects in FireBreath must be used in a boost::shared_ptr; as a convenience, typedefs are provided for each commonly used type with the suffix Ptr appended:

Code Block
#include "variant_list.h"

void MyPluginAPI::doCallback(const std::string& a, const FB::JSObjectPtr& callback) {
    callback->InvokeAsync("", FB::variant_list_of(a)("Param2")(3));
    // Note that if callback is an actual JS function, "" should be used as the method name
    // when calling Invoke or InvokeAsync
}

As always, the registerMethod call in the constructor is exactly the same. The most common use-case for this feature is to perform a task that takes a long time on another thread and then call the callback when finished; in that case you would want to save the callback so you have it later when you need it.

JavaScript value objects

If you need to pass a "dictionary" object from Javascript, you can do that by using a std::map (or any other STL dictionary type, such as hash_map, multimap, etc) as your parameter type. The key should be a std::string, but the value can be any type supported by JSAPIAuto.

Code Block
void MyPluginAPI::setup(const std::map<std::string,std::string>& settings) {
    // Read settings, do something useful
}

Note that this is a pass-by-value only situation; if you need to pass the object by reference then you should accept a FB::JSObjectPtr and use Invoke, GetProperty, and SetProperty to manipulate it.

JavaScript arrays

If you need to pass an array from Javascript, you can do that by using a std::vector (or any other STL list type, such as list, set, etc) as your parameter type. The template type of the container can be any supported JSAPI type

Code Block
void MyPluginAPI::setFlags(const std::list<std::bool>& flags) {
    // Read flags, do something useful
}

Note that this is a pass-by-value only situation; if you need to pass the object by reference then you should accept a FB::JSObjectPtr and use Invoke, GetProperty, and SetProperty to manipulate it -- remember that arrays are just objects in JavaScript.

More types

For a complete list of supported types, see Supported JSAPI types