2016-03-22 12 views
1

Ich arbeite an einer Chrome-Erweiterung, die die indexedDB-Wrapper Dexie, mehrere jQuery-Bibliotheken und syncfusions eGrid zum Anzeigen und Ändern der Datenbank verwendet. Ich weiß, dass diese Frage schon mehrmals gestellt wurde, aber jetzt entdeckte ich ein seltsames Verhalten.Mehrere chrome.runtime.onMessage-Listeners - getrennte Port-Fehler

Mein Setup:

  • Hintergrund-Skript
  • content-Skript (in eine Webseite injiziert)
  • index.html (für das Grid Anzeigen)
  • manifest.json

Wenn ich meine Erweiterung neu lade, ist alles in Ordnung. Ich gebe eine scrollbare Liste von Namen mit meinem Inhaltsskript ein. Das funktioniert auch, wenn ich den Tab neu lade. Aber als ich die index.html vor öffnete (popup.html in der offiziellen Dokumentation) die sendResponse von background-script funktioniert nach einiger Zeit nicht mehr. Das erneute Laden der Registerkarte würde dann nicht helfen. Ich suchte stundenlang, fand viele ähnliche Fälle und einige Einträge zu googles bugtracker, wo sie behaupteten, dass dieses Problem bereits behoben sei. Ich weiß auch, dass ich Return true verwenden muss, wenn ich async Funktionen wie mit indexedDB habe.

Um Ihnen weitere Informationen zu geben, werde ich meine verkürzten Skripte auflisten.

Inhalt-Script

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); 
    }); 
}); 

Background-Script

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'" 
} 

Es ist schwierig für mich, dieses Verhalten zu debuggen, da ich genau kann nicht wissen, wann die Fehler Attempting to use a disconnected port object erscheint. Es sieht so aus, als würde der Port nicht korrekt geschlossen, nachdem die sendResponse ausgeführt wurde und nur wenn die index.html geöffnet wurde.

Schließlich ist die verkürzte Fehlermeldung:

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) 

Die IDB-Funktionen sind individuell und won't Arbeit, wie ich sie hier geschrieben habe, aber ich will nur zeigen, was ich bin es zu tun. Ich verwende .then() bis sendReponse, wenn das Ergebnis verfügbar ist und return true; auf das Ergebnis warten, wenn async. Ich habe auch jedes Skript nach onMessage und sendMessage durchsucht. Die aufgelisteten Funktionen, die sich auf onMessage/sendMessage beziehen, sind alles, was ich verwende, sie werden nicht woanders verwendet. Ich verwende einen Timer für indexhtml(), um ein Array zu speichern, das die Benachrichtigungen in einem bestimmten Intervall enthält, und dann eine Funktion in index.html auszuführen, um die Benachrichtigungen neu zu laden.

Der Index.html Tab wird über browserAction geöffnet:

chrome.browserAction.onClicked.addListener(function(tab){ 
    chrome.tabs.create({url: chrome.extension.getURL('index.html')}); 
}); 

Antwort

3

Okay, ich fand die Antwort von mir nach dem Hinzufügen von mehr und mehr Debugging. Das Problem hier ist, dass ich mehrere (2) onMessage-Listener verwende. Es gibt keinen bestimmten Listener, der nur auf Nachrichten wartet, die an meine Inhaltsseite (index.html) gesendet werden. Jedes Mal, wenn ich den index.html öffne, würde der zusätzliche onMessage-Listener dann zuerst aufgerufen werden, wenn sendMessage in meinem content-script verwendet würde. Mir war nicht bewusst, dass dieser onMessage-Listener, eingebettet in index.html, auch immer aufgerufen wird. Ich habe dann das Ziel für sendMessage in meinem Content-Skript geändert - mit dem gleichen Ergebnis. Es gibt auch einen Hinweis auf Googles Dokumentation zu diesem Thema:

Hinweis: Wenn mehrere Seiten für onMessage Ereignisse hören, nur die erste sendresponse() für ein bestimmtes Ereignis nennen in Senden die Antwort erfolgreich sein wird. Alle anderen Antworten auf dieses Ereignis werden ignoriert.

Link here

Das zweite Problem war, dass ich außerhalb der if-Klausel der sendresponse verwendet und auf diese Weise es jedes Mal aufgerufen wurde. Also musste ich am Ende nur die sendResponse in die if-Klausel setzen, damit sie nur aufgerufen wird, wenn eine bestimmte Nachricht von der Hintergrundseite über sendMessage aufgerufen wurde.

Background-Script

  • erhalten die tab.id durch die Chrom-Erweiterung suchen: //extid/index.html
  • die Nachricht senden nur index.html
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 
     } 
});