Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A firebreath JSAPI will not be recognized in firefox

Tags:

firebreath

First goes some background about what I am doing with Firebreath.

  1. I am developing a rendering viewer plugin in browser by using firebreath.
  2. I define two MIME type in my plugin, one is for the main viewer, and the other is for 2D plan view.
  3. In each page, only one main viewer is allowed, but can have multiple 2D plan view. And they all share the same model document opened in main viewer.
  4. So, after instantiating 2D plan view, I need to pass the document object (a firebreath JSAPI) to the 2d plan view.

Then, assume the main viewer and plan view are both loaded named as 'mainviewer' and 'planview', and I will attch the document to plan viewer as below,

planview.attach(mainviewer.doc); 
(the signature is "bool attach(const FB::JSObjectPtr& myDoc)" and 
The mainviewer.doc is just a firebreath JSAPI)

The ISSUE is that in firefox, the passed JSObject can't be recognized as a JSAPI by calling

FB::JSAPIPtr jsAPI = myDoc->getJSAPI(); // THIS WILL RETURN **NULL**.
m_main_doc = FB::ptr_cast<LcFbViewerDocumentAPI>(jsAPI); // Cast to my document API.

This issue only happens when the host browser is firefox, IE/Chrome works well.

So, what happened to the passed JSAPI when using firefox?

like image 677
Wayne Wang Avatar asked Oct 04 '22 19:10

Wayne Wang


1 Answers

As it turns out, most browsers (including FireFox) wrap NPObjects before letting them be passed into another function call; because of this, you can't get at the underlying C++ class that you originally passed to the browser. Because FireBreath can't get to the real NPJavascriptObject (the NPObject that FireBreath uses to wrap JSAPI objects to give to the browser) it can't get to the original JSAPI object either.

Consider creating a static id for each instance of your JSAPI object. You can then expose the instance_id as a JSAPI property and then create a global std::map that you can use to store a map to get at your object.

// in the class def
static int counter;
int instance_id;

// In the .cpp file
int MyPluginAPI::counter(0);

std::map<int, FB::JSAPIWeakPtr> apiMap;
FB::JSAPIPtr getJSAPIObjectById(int id) {
    std::map<int, FB::JSAPIWeakPtr> fnd = apiMap.find(id);
    if (fnd != apiMap.end()) {
        return fnd.second->lock(); // it's a weak pointer, lock to get the shared_ptr
    } else {
        return FB::JSAPIPtr(); // Alternately throw an exception
    }
}

MyPluginAPI::MyPluginAPI() {
    instance_id = counter++;
    // Note that you can't get at the shared_ptr in the constructor,
    // so you'll have to call an init function after creating the JSAPI object

    registerProperty("instance_id",
                 make_property(this,
                    &FBTestPluginAPI::get_instId));
}

int MyPluginAPI::get_instId() { return instance_id; }

void MyPluginAPI::init() {
    apiMap[instance_id] = shared_from_this();
}

This would of course eventually leak a small amount of memory if you don't ever go through the map and clear out expired weak ptrs, but it should give you what you need. When you get an object that should be a JSAPIPtr object you can expect it as a JSObjectPtr.

void doSomethingWithAnAPI(const FB::JSObjectPtr& obj) {
    if (obj) {
        int id = obj->GetProperty("instance_id");
        FB::JSAPIPtr ptr = getJSAPIObjectById(id);
        if (ptr) {
            // Hurray! We have the object
        }
    }
}

I haven't tested the above code, but it should be pretty close.

like image 198
taxilian Avatar answered Oct 10 '22 02:10

taxilian