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