Monday, 1 August 2022

Unable to maintain a long lived connection between chrome extension and native app

I am trying to maintain a long lived connection from background.js as 1 port per tab. The background.js in my chrome extension exchange messages with html webpage per tab basis on a port. The webpage has got some iframes that vary in numbers and load dynamically.

Inorder to establish and persist connection, I am preserving the port and associated frame as a value in a dictionary where tabid is the key. I am doing this while connecting to native app.

portDict[tabId] = { port: port, frames: [frame] };

Similarly, while disconnecting to the native app, I am removing Iframe from the port and deleting the tabId from the port dictionary. After doing these changes I have been able to maintain and persist the port connection with tab and Iframes to a great extent but unfortunately the logic only works 4/5 times.

In simple words, there is some inconsistency in my code due to which sometimes the connection breaks while communicating to the native host. I am unable to figure out what is wrong here. Any help would be highly appreciated.

Here is my complete code :

The Background.js listens for messages from content script.js like this :

chrome.runtime.onMessage.addListener(
    function (request, sender, sendResponse) {
        try {
            var frameId = sender.frameId || 0;
            
            if (request.type == "connect") {
                connect(sender.tab,frameId);
                sendResponse({ success: true });
            } else if (request.type == "disconnect") {              
                disconnect(sender.tab,frameId);
                sendResponse({ success: true });
            }else if (request.type == "send") {
                var tab = sender.tab;
                var tabid = tab.id;
                var port = portDict[tabid];
                if (port == null || port == '') {
                    connect(tab, frameId);
                }
                sendNativeMessage(request.data, sender.tab);
              } 
            return true;
        }
        catch (e) {
            sendResponse({ success: false, message: e.message })
            return true;
        }
    });

The background.js connects to native app and get the communication port

function connect(tab, frame) {
    var tabid = tab.id;
    var portObj = portDict[tabid];

    if (portObj != null && portObj.port != null && !portObj.port.error) {
        return;
    }

    var port = null;
    try {
        connectedString = "com.xyz.nativemessaging";
        port = browser.runtime.connectNative(connectedString);       
    }
    catch (e) {
        console.log('Failed to connect to com.xyz.nativemessaging');
        console.log(e);
        port = null;
    }

    port.onMessage.addListener(onNativeMessage);
    port.onDisconnect.addListener(onDisconnected);

    var id = tab.id;
    portDict[id] = { port: port, frames: [frame] };
}

The background.js disconnects from Native app like this :

function disconnect(tab, frameid) {
    if (removeFrameFromPort(tab, frameid)) {     
        sendNativeMessage("disconnect", tab);
        var id = tab.id;      
        delete portDict[id];
    }
}

function removeFrameFromPort(tab, frame) {
    var tabId = tab.id;
    try {
        var portObj = portDict[tabId];
        if (portObj) {
            portObj.frames = portObj.frames.filter(x=>x != frame);
            return portObj.frames.length === 0;
        }
    }
    catch (e) {
        throw e;
    }
    return false;
}

The background.js sends the message to native app like this

function sendNativeMessage(message, tab) {
    var tabId = tab.id;
    try {
        message["tabId"] = tabId;
        var portObj = portDict[tabId];
        portObj.port.postMessage(message);
    }
    catch (e) {
        throw e;
    }
}

The onDisconnected

function onDisconnected(port) {

    console.log('Port Disconnected ' + port.id);

    browser.tabs.query({ active: true, currentWindow: true }, function (tabs) {
        if (typeof port.id !== "undefined") {
            var lastTabId = tabs[0].id;
            browser.tabs.sendMessage(lastTabId, "disconnected");
        }
    });
}

The reason why I am currently storing port and frames as a value in dictionary

portDict[id] = { port: port, frames: [frame] }; 

is because it matches the Nativehost where tabId is the key. The Nativehost reads the deserialized data as a dictionary and gets value based on tabId as key.



from Unable to maintain a long lived connection between chrome extension and native app

No comments:

Post a Comment