Bericht overslaan

Omdat inhoudsscripts in de context van een webpagina worden uitgevoerd en niet in de extensie, hebben ze vaak een manier nodig om met de rest van de extensie te communiceren. Een RSS-lezerextensie kan bijvoorbeeld inhoudsscripts gebruiken om de aanwezigheid van een RSS-feed op een pagina te detecteren en vervolgens de achtergrondpagina op de hoogte te stellen om een ​​pagina-actiepictogram voor die pagina weer te geven.

De communicatie tussen extensies en hun inhoudsscripts werkt via het doorgeven van berichten. Beide partijen kunnen luisteren naar berichten die vanaf de andere kant worden verzonden en op hetzelfde kanaal reageren. Een bericht kan elk geldig JSON-object bevatten (null, boolean, getal, tekenreeks, array of object). Er is een eenvoudige API voor eenmalige verzoeken en een complexere API waarmee u langdurige verbindingen kunt hebben voor het uitwisselen van meerdere berichten met een gedeelde context. Het is ook mogelijk om een ​​bericht naar een ander toestel te sturen als u de ID kent. Dit wordt besproken in de sectie Berichten over meerdere toestellen .

Eenvoudige eenmalige aanvragen

Als u slechts één bericht naar een ander deel van uw extensie hoeft te sturen (en optioneel een reactie terugkrijgt), moet u de vereenvoudigde runtime.sendMessage of tabs.sendMessage gebruiken. Hiermee kunt u een eenmalig JSON-serialiseerbaar bericht verzenden van respectievelijk een inhoudsscript naar extension , of omgekeerd. Met een optionele callback-parameter kunt u het antwoord van de andere kant afhandelen, als die er is.

Het verzenden van een verzoek vanuit een inhoudsscript ziet er als volgt uit:

chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
  console.log(response.farewell);
});

Het verzenden van een verzoek van de extensie naar een inhoudsscript lijkt erg op elkaar, behalve dat u moet opgeven naar welk tabblad het moet worden verzonden. Dit voorbeeld demonstreert het verzenden van een bericht naar het inhoudsscript op het geselecteerde tabblad.

chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
  chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
    console.log(response.farewell);
  });
});

Aan de ontvangende kant moet u een runtime.onMessage -gebeurtenislistener instellen om het bericht af te handelen. Dit ziet er hetzelfde uit vanaf een inhoudsscript of extensiepagina.

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    console.log(sender.tab ?
                "from a content script:" + sender.tab.url :
                "from the extension");
    if (request.greeting == "hello")
      sendResponse({farewell: "goodbye"});
  }
);

In het bovenstaande voorbeeld werd sendResponse synchroon aangeroepen. Als u sendResponse asynchroon wilt gebruiken, voegt u return true; naar de onMessage gebeurtenishandler.

Opmerking: als meerdere pagina's luisteren naar onMessage-gebeurtenissen, zal alleen de eerste die sendResponse() aanroept voor een bepaalde gebeurtenis erin slagen het antwoord te verzenden. Alle andere reacties op die gebeurtenis worden genegeerd.
Opmerking: De sendResponse callback is alleen geldig als deze synchroon wordt gebruikt, of als de gebeurtenishandler true retourneert om aan te geven dat deze asynchroon zal reageren. De callback van de functie sendMessage wordt automatisch aangeroepen als er geen handlers true retourneren of als de callback sendResponse garbage-collected is.

Verbindingen met een lange levensduur

Soms is het handig om een ​​gesprek te voeren dat langer duurt dan één enkel verzoek en antwoord. In dit geval kunt u een kanaal met een lange levensduur openen van uw inhoudsscript naar een extensiepagina , of omgekeerd, met behulp van respectievelijk runtime.connect of tabs.connect . Optioneel kan het kanaal een naam krijgen, waardoor je onderscheid kunt maken tussen verschillende soorten verbindingen.

Een gebruiksscenario kan een extensie voor het automatisch invullen van formulieren zijn. Het inhoudsscript kan voor een bepaalde login een kanaal naar de extensiepagina openen en voor elk invoerelement op de pagina een bericht naar de extensie sturen met het verzoek om formuliergegevens in te vullen. Dankzij de gedeelde verbinding kan de extensie de gedeelde status blijven behouden. de verschillende berichten die uit het inhoudsscript komen.

Bij het tot stand brengen van een verbinding krijgt elk uiteinde een runtime.Port -object dat wordt gebruikt voor het verzenden en ontvangen van berichten via die verbinding.

Zo opent u een kanaal vanuit een inhoudsscript en verzendt en luistert u naar berichten:

var port = chrome.runtime.connect({name: "knockknock"});
port.postMessage({joke: "Knock knock"});
port.onMessage.addListener(function(msg) {
  if (msg.question == "Who's there?")
    port.postMessage({answer: "Madame"});
  else if (msg.question == "Madame who?")
    port.postMessage({answer: "Madame... Bovary"});
});

Het verzenden van een verzoek van de extensie naar een inhoudsscript lijkt erg op elkaar, behalve dat u moet opgeven met welk tabblad u verbinding wilt maken. Vervang eenvoudigweg de oproep om verbinding te maken in het bovenstaande voorbeeld met tabs.connect .

Om inkomende verbindingen te kunnen afhandelen, moet u een runtime.onConnect -gebeurtenislistener instellen. Dit ziet er hetzelfde uit vanuit een inhoudsscript of een extensiepagina. Wanneer een ander deel van uw extensie "connect()" aanroept, wordt deze gebeurtenis geactiveerd, samen met het runtime.Port -object dat u kunt gebruiken om berichten via de verbinding te verzenden en te ontvangen. Zo ziet het eruit om te reageren op inkomende verbindingen:

chrome.runtime.onConnect.addListener(function(port) {
  console.assert(port.name == "knockknock");
  port.onMessage.addListener(function(msg) {
    if (msg.joke == "Knock knock")
      port.postMessage({question: "Who's there?"});
    else if (msg.answer == "Madame")
      port.postMessage({question: "Madame who?"});
    else if (msg.answer == "Madame... Bovary")
      port.postMessage({question: "I don't get it."});
  });
});

Levensduur van de haven

Poorten zijn ontworpen als tweerichtingscommunicatiemethode tussen verschillende delen van de extensie, waarbij een (top-level) frame als het kleinste onderdeel wordt gezien. Bij het aanroepen van tabs.connect , runtime.connect of runtime.connectNative wordt er een poort gemaakt. Deze poort kan direct worden gebruikt voor het verzenden van berichten naar de andere kant via postMessage .

Als er meerdere frames op een tabblad staan, resulteert het aanroepen van tabs.connect in meerdere aanroepen van de runtime.onConnect- gebeurtenis (één keer voor elk frame op het tabblad). Op dezelfde manier, als runtime.connect wordt gebruikt, kan de onConnect-gebeurtenis meerdere keren worden geactiveerd (één keer voor elk frame in het uitbreidingsproces).

Mogelijk wilt u weten wanneer een verbinding is gesloten, bijvoorbeeld of u voor elke open poort een afzonderlijke status handhaaft. Hiervoor kunt u luisteren naar de runtime.Port.onDisconnect -gebeurtenis. Deze gebeurtenis wordt geactiveerd als er geen geldige poorten zijn aan de andere kant van het kanaal. Dit gebeurt in de volgende situaties:

  • Er zijn geen luisteraars voor runtime.onConnect aan de andere kant.
  • Het tabblad met de poort wordt verwijderd (bijvoorbeeld als er op het tabblad wordt genavigeerd).
  • Het frame van waaruit connect werd aangeroepen, is verwijderd.
  • Alle frames die de poort hebben ontvangen (via runtime.onConnect ) zijn gelost.
  • runtime.Port.disconnect wordt aangeroepen door het andere uiteinde . Houd er rekening mee dat als een connect aanroep resulteert in meerdere poorten aan de kant van de ontvanger, en disconnect() wordt aangeroepen op een van deze poorten, de gebeurtenis onDisconnect alleen wordt geactiveerd op de poort van de afzender, en niet op de andere poorten.

Berichten over meerdere extensies

Naast het verzenden van berichten tussen verschillende componenten in uw extensie, kunt u de berichten-API gebruiken om met andere extensies te communiceren. Hierdoor kunt u een openbare API beschikbaar stellen waar andere extensies van kunnen profiteren.

Het luisteren naar binnenkomende aanvragen en verbindingen is vergelijkbaar met het interne geval, behalve dat u de methoden runtime.onMessageExternal of runtime.onConnectExternal gebruikt. Hier is een voorbeeld van elk:

// For simple requests:
chrome.runtime.onMessageExternal.addListener(
  function(request, sender, sendResponse) {
    if (sender.id == blocklistedExtension)
      return;  // don't allow this extension access
    else if (request.getTargetData)
      sendResponse({targetData: targetData});
    else if (request.activateLasers) {
      var success = activateLasers();
      sendResponse({activateLasers: success});
    }
  });

// For long-lived connections:
chrome.runtime.onConnectExternal.addListener(function(port) {
  port.onMessage.addListener(function(msg) {
    // See other examples for sample onMessage handlers.
  });
});

Op dezelfde manier is het verzenden van een bericht naar een ander toestel vergelijkbaar met het verzenden van een bericht binnen uw toestel. Het enige verschil is dat u de ID moet doorgeven van het toestel waarmee u wilt communiceren. Bijvoorbeeld:

// The ID of the extension we want to talk to.
var laserExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";

// Make a simple request:
chrome.runtime.sendMessage(laserExtensionId, {getTargetData: true},
  function(response) {
    if (targetInRange(response.targetData))
      chrome.runtime.sendMessage(laserExtensionId, {activateLasers: true});
  }
);

// Start a long-running conversation:
var port = chrome.runtime.connect(laserExtensionId);
port.postMessage(...);

Berichten verzenden vanaf webpagina's

Net als bij berichten over meerdere extensies kan uw app of extensie berichten van gewone webpagina's ontvangen en erop reageren. Om deze functie te gebruiken, moet u eerst in uw manifest.json opgeven met welke websites u wilt communiceren. Bijvoorbeeld:

"externally_connectable": {
  "matches": ["*://*.example.com/*"]
}

Hierdoor wordt de berichten-API zichtbaar op elke pagina die overeenkomt met de URL-patronen die u opgeeft. Het URL-patroon moet ten minste een domein op het tweede niveau bevatten. Dit betekent dat hostnaampatronen zoals "*", "*.com", "*.co.uk" en "*.appspot.com" niet zijn toegestaan. Gebruik vanaf de webpagina de API's runtime.sendMessage of runtime.connect om een ​​bericht naar een specifieke app of extensie te sturen. Bijvoorbeeld:

// The ID of the extension we want to talk to.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";

// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
  function(response) {
    if (!response.success)
      handleError(url);
  });

Vanuit uw app of extensie kunt u naar berichten van webpagina's luisteren via de runtime.onMessageExternal of runtime.onConnectExternal API's, vergelijkbaar met berichtenuitwisseling tussen extensies . Alleen de webpagina kan een verbinding tot stand brengen. Hier is een voorbeeld:

chrome.runtime.onMessageExternal.addListener(
  function(request, sender, sendResponse) {
    if (sender.url == blocklistedWebsite)
      return;  // don't allow this web page access
    if (request.openUrlInEditor)
      openUrl(request.openUrlInEditor);
  });

Native berichten

Extensies en apps kunnen berichten uitwisselen met native applicaties die zijn geregistreerd als native messaging host . Zie Native messaging voor meer informatie over deze functie.

Beveiligingsoverwegingen

Inhoudsscripts zijn minder betrouwbaar

Inhoudsscripts zijn minder betrouwbaar dan de achtergrondpagina van de extensie (een kwaadwillende webpagina kan bijvoorbeeld het weergaveproces waarin de inhoudsscripts worden uitgevoerd, in gevaar brengen). Ga ervan uit dat berichten uit een inhoudsscript mogelijk door een aanvaller zijn gemaakt en zorg ervoor dat u alle invoer valideert en opschoont . Stel dat gegevens die naar het inhoudsscript worden verzonden, naar de webpagina kunnen lekken. Beperk het bereik van bevoorrechte acties die kunnen worden geactiveerd door berichten die worden ontvangen van inhoudsscripts.

Cross-site scripting

Wanneer u een bericht ontvangt van een inhoudsscript of een andere extensie, moeten uw scripts erop letten dat ze niet het slachtoffer worden van cross-site scripting . Dit advies is van toepassing op scripts die op de achtergrondpagina van de extensie worden uitgevoerd, maar ook op inhoudsscripts die op andere webbronnen worden uitgevoerd. Vermijd in het bijzonder het gebruik van gevaarlijke API's zoals de onderstaande:

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // WARNING! Might be evaluating an evil script!
  var resp = eval("(" + response.farewell + ")");
});
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // WARNING! Might be injecting a malicious script!
  document.getElementById("resp").innerHTML = response.farewell;
});

Geef in plaats daarvan de voorkeur aan veiligere API's die geen scripts uitvoeren:

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // JSON.parse does not evaluate the attacker's scripts.
  var resp = JSON.parse(response.farewell);
});
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // innerText does not let the attacker inject HTML elements.
  document.getElementById("resp").innerText = response.farewell;
});

Voorbeelden

Eenvoudige voorbeelden van communicatie via berichten vindt u in de map example/api/messaging . Het native messaging-voorbeeld laat zien hoe een Chrome-app kan communiceren met een native app. Zie Voorbeelden voor meer voorbeelden en hulp bij het bekijken van de broncode.