javascript - Flera chrome.runtime.onMessage-lyssnare - frånkopplat portfel
Jag arbetar på en Chrome-tillägg som använder indexedDB-Wrapper Dexie, flera jQuery-bibliotek och Syncfusion's eGrid för att visa och ändra databasen. Jag vet att denna fråga har ställts flera gånger tidigare, men nu har jag upptäckt ett konstigt beteende.
Min uppställning:
- bakgrundsskript
- innehållsskript (injicerat på en webbplats)
- index.html (för att visa rutnätet)
- manifest.json
När jag laddar om min extension fungerar allt fint. Jag injicerar en scrollbar lista med namn med mitt content-script. Detta fungerar också när jag laddar om fliken. Men när jag öppnade index.html innan (popup.html i den officiella dokumentationen) slutar sendResponse från background-script att fungera efter ett tag. Att ladda om fliken skulle då inte hjälpa. Jag letade i timmar, hittade många liknande fall och några poster på Googles bugtracker där de hävdade att problemet redan var fixat. Jag vet också att jag måste använda return true om jag har asynkrona funktioner som med indexedDB.
Så för att ge dig mer information kommer jag att lista mina förkortade manus.
Innehållsskript
chrome.runtime.sendMessage({greeting: 'getNames'},function(response){
$('.names-container').append(response.farewell);
});
chrome.runtime.sendMessage({greeting: 'getData'+name},function(name){
chrome.runtime.sendMessage({greeting: 'delete'},function(response){
console.log(response.farewell);
});
chrome.runtime.sendMessage({greeting: 'set'}, function(response) {
console.log(response.farewell);
});
});
Bakgrundsskript
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse){
if(message.greeting == 'getNames'){
// Get array of names from idb and send it back
idb.opendb().then(function(a){
sendResponse(a);
});
return true;
} else if(message.greeting.indexOf('getData') >= 0){
// get more information for selected name
idb.opendb().then(function(a){
sendResponse(a);
});
return true;
} else if(message.greeting == 'delete'){
// further processing to delete some stuff
idb.opendb().then(function(a){
sendResponse(a);
});
return true;
} else if(message.greeting == 'set'){
// synchronous function
// do something ...
return false;
} else {
// Do something
}
//return true;
});
funtion indexhtml(){
chrome.runtime.sendMessage(
"notifications",
function (response) {
console.log(response);
}
);
}
Index.html
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){
if(request == 'notifications'){
notifications();
}
sendResponse("Message received from background-page for "+request);
});
Manifest
{
"manifest_version": 2,
"name": "MyExtension",
"description": "Some desc...",
"version": "0.2",
"background": {
"scripts": ["js/jquery-2.1.4.min.js", "js/background.js", "...",
"persistent": true
},
"default_locale": "en",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/jquery-2.1.4.min.js"]
},
{
"matches": ["https://domain.tld/*"],
"js": ["js/jquery-2.1.4.min.js", "js/contentscript.js"],
"css" : ["css/contentscript.css",
"run_at": "document_start"
}
],
"icons": {
"16": "images/icon-16x16.png",
"48": "images/icon-48x48.png",
"128": "images/icon-128x128.png"
},
"browser_action": {
"default_icon": "images/icon-128x128.png"
},
"permissions": [
"cookies", "tabs", "alarms", "webRequest", "webRequestBlocking",
"contextMenus", "<all_urls>", "storage"
],
"content_security_policy": "script-src 'self' 'unsafe-eval';
object-src 'self'"
}
Det är svårt för mig att felsöka detta beteende, eftersom jag inte exakt kan veta när felet Attempting to use a disconnected port object uppstår. Det ser ut som att porten inte stängs korrekt efter att sendResponse har körts och endast om index.html har öppnats.
Slutligen det förkortade felmeddelandet:
Error: Attempting to use a disconnected port object
at Error (native)
at PortImpl.postMessage (extensions::messaging:65:22)
at Port.publicClass.(anonymous function) [as postMessage] (extensions::utils:94:26)
at responseCallback (extensions::messaging:163:16)
idb-funktionerna är anpassade och kommer inte att fungera som jag skrev dem här, men jag vill bara visa vad jag gör där. Jag använder .then() för att sendResponse när resultatet är tillgängligt och return true; för att vänta på resultatet om det är asynkront. Jag har också sökt igenom varje skript efter onMessage och sendMessage. De listade funktionerna relaterade till onMessage/sendMessage är allt jag använder, de används inte någon annanstans. Jag använder en timer för indexhtml() för att spara en array som innehåller notifikationerna i ett specificerat intervall och sedan köra en funktion i index.html för att ladda om notifikationerna.
Fliken index.html öppnas via browserAction:
chrome.browserAction.onClicked.addListener(function(tab){
chrome.tabs.create({url: chrome.extension.getURL('index.html')});
});
Svar #1
Okej, jag hittade svaret själv efter att ha lagt till mer och mer felsökning. Problemet här är att jag använder flera (2) onMessage-lyssnare. Det finns ingen specifik lyssnare som bara lyssnar på meddelanden som skickas till min innehållssida (index.html). Varje gång jag öppnade index.html skulle den extra onMessage-lyssnaren då kallas först om sendMessage användes inom mitt innehållsskript. Jag var inte medveten om att denna onMessage-lyssnare, inbäddad i index.html, också kallas varje gång. Jag ändrade sedan destinationen för sendMessage i mitt innehållsskript - med samma resultat. Det finns också en notering i Googles dokumentation om detta ämne:
Observera: Om flera sidor lyssnar på onMessage-händelser kommer endast den första som anropar sendResponse() för en viss händelse att lyckas skicka svaret. Alla andra svar på den händelsen kommer att ignoreras.
Länk här
Det andra problemet var att jag använde sendResponse utanför if-satsen och på det sättet blev den anropad varje gång. Så i slutändan behövde jag bara lägga sendResponse inuti if-satsen så att den bara anropas om ett specifikt meddelande från bakgrundssidan blev anropad via sendMessage.
Bakgrundsskript
- få tab.id genom att söka efter chrome-extension://extid/index.html
- skicka meddelandet till index.html endast
chrome.windows.getAll({populate:true},function(windows){
windows.forEach(function(window){
window.tabs.forEach(function(tab){
if(tab.url.indexOf(chrome.extension.getURL('index.html')) >= 0){
chrome.tabs.sendMessage(tab.id,{ greeting: "notifications"}, function(response){
console.log(response);
});
}
});
});
});
Index.html
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
if(message.greeting == "notifications-trades"){
Notifications();
sendResponse("Message received from bg "+message.greeting);
//if async add return true;
} else {
//Do something
}
});
