Bericht overslaan

Met berichten-API's kunt u communiceren tussen verschillende scripts die worden uitgevoerd in contexten die aan uw extensie zijn gekoppeld. Dit omvat communicatie tussen uw service worker, chrome-extension://pages en contentscripts. Een RSS-reader kan bijvoorbeeld contentscripts gebruiken om de aanwezigheid van een RSS-feed op een pagina te detecteren en de service worker vervolgens te vragen het actiepictogram voor die pagina bij te werken.

Er zijn twee API's voor het doorgeven van berichten: één voor eenmalige verzoeken en een complexere API voor langdurige verbindingen waarmee meerdere berichten kunnen worden verzonden.

Zie het gedeelte over berichten tussen extensies voor informatie over het verzenden van berichten tussen extensies .

Eenmalige verzoeken

Om een ​​enkel bericht naar een ander deel van uw extensie te sturen en optioneel een antwoord te ontvangen, roept u runtime.sendMessage() of tabs.sendMessage() aan. Met deze methoden kunt u eenmalig een JSON-serialiseerbaar bericht van een contentscript naar de extensie sturen, of van de extensie naar een contentscript. Beide API's retourneren een Promise die resulteert in het antwoord van een ontvanger.

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

inhoud-script.js:

(async () => {
  const response = await chrome.runtime.sendMessage({greeting: "hello"});
  // do something with response here, not outside the function
  console.log(response);
})();

Reacties

Gebruik de gebeurtenis chrome.runtime.onMessage om naar een bericht te luisteren:

// Event listener
function handleMessages(message, sender, sendResponse) {
  fetch(message.url)
    .then((response) => sendResponse({statusCode: response.status}))

  // Since `fetch` is asynchronous, must return an explicit `true`
  return true;
}

chrome.runtime.onMessage.addListener(handleMessages);

// From the sender's context...
const {statusCode} = await chrome.runtime.sendMessage({
  url: 'https://example.com'
});

Wanneer de eventlistener wordt aangeroepen, wordt een sendResponse -functie als derde parameter doorgegeven. Deze functie kan worden aangeroepen om een ​​respons te geven. Standaard moet de sendResponse callback synchroon worden aangeroepen. Als u asynchroon wilt werken om de waarde die aan sendResponse wordt doorgegeven te verkrijgen, moet u een letterlijke true (niet alleen een waarheidsgetrouwe waarde) van de eventlistener retourneren. Hierdoor blijft het berichtenkanaal open voor de andere kant totdat sendResponse wordt aangeroepen.

Als u sendResponse aanroept zonder parameters, wordt null als antwoord verzonden.

Als meerdere pagina's luisteren naar onMessage gebeurtenissen, zal alleen de eerste pagina die sendResponse() voor een bepaalde gebeurtenis aanroept, de respons kunnen verzenden. Alle andere responsen op die gebeurtenis worden genegeerd.

Langdurige verbindingen

Om een ​​herbruikbaar kanaal voor het doorgeven van berichten met een lange levensduur te creëren, roept u het volgende aan:

  • runtime.connect() om berichten van een inhoudsscript door te geven aan een extensiepagina
  • tabs.connect() om berichten van een extensiepagina door te geven aan een inhoudsscript.

U kunt uw kanaal een naam geven door een options-parameter met een name door te geven, zodat u onderscheid kunt maken tussen verschillende soorten verbindingen:

const port = chrome.runtime.connect({name: "example"});

Een mogelijke toepassing voor een langdurige verbinding is een extensie voor het automatisch invullen van formulieren. Het contentscript kan een kanaal naar de extensiepagina openen voor een specifieke login en voor elk invoerelement op de pagina een bericht naar de extensie sturen om de in te vullen formuliergegevens op te vragen. Dankzij de gedeelde verbinding kan de extensie de status delen tussen extensiecomponenten.

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

Gebruik de volgende code om een ​​kanaal te openen vanuit een inhoudsscript en om berichten te verzenden en ernaar te luisteren:

inhoud-script.js:

const port = chrome.runtime.connect({name: "knockknock"});
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"});
  }
});
port.postMessage({joke: "Knock knock"});

Om een ​​verzoek van de extensie naar een inhoudsscript te sturen, vervangt u de aanroep van runtime.connect() in het vorige voorbeeld door tabs.connect() .

Om inkomende verbindingen voor een contentscript of een extensiepagina te verwerken, stelt u een runtime.onConnect -gebeurtenislistener in. Wanneer een ander onderdeel van uw extensie connect() aanroept, worden deze gebeurtenis en het runtime.Port -object geactiveerd. De code voor het reageren op inkomende verbindingen ziet er als volgt uit:

service-worker.js:

chrome.runtime.onConnect.addListener(function(port) {
  if (port.name !== "knockknock") {
    return;
  }
  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."});
    }
  });
});

Serialisatie

In Chrome gebruiken de API's voor berichtdoorgifte JSON-serialisatie. Dit betekent dat een bericht (en de antwoorden van ontvangers) elke geldige JSON-waarde (null, boolean, number, string, array of object) kan bevatten. Andere waarden worden omgezet in serialiseerbare waarden.

Opvallend is dat dit verschilt van andere browsers die dezelfde API's implementeren met het gestructureerde kloonalgoritme.

Levensduur van de poort

Poorten zijn ontworpen als een tweerichtingscommunicatiemechanisme tussen verschillende onderdelen van een extensie. Wanneer een onderdeel van een extensie tabs.connect() , runtime.connect() of runtime.connectNative() aanroept, wordt er een poort aangemaakt die direct berichten kan verzenden met behulp van postMessage() .

Als er meerdere frames in een tabblad staan, wordt door het aanroepen van tabs.connect() de gebeurtenis runtime.onConnect één keer voor elk frame in het tabblad aangeroepen. Als runtime.connect() wordt aangeroepen, kan de gebeurtenis onConnect ook één keer voor elk frame in het extensieproces worden geactiveerd.

U wilt mogelijk weten wanneer een verbinding wordt gesloten, bijvoorbeeld als u voor elke open poort een aparte status bijhoudt. Luister hiervoor naar de gebeurtenis runtime.Port.onDisconnect . Deze gebeurtenis wordt geactiveerd wanneer er geen geldige poorten aan de andere kant van het kanaal zijn, wat een van de volgende oorzaken kan hebben:

  • Er zijn geen listeners voor runtime.onConnect aan de andere kant.
  • Het tabblad met de poort wordt verwijderd (bijvoorbeeld als er door het tabblad wordt genavigeerd).
  • Het frame waarin connect() werd aangeroepen, is ontladen.
  • Alle frames die de poort hebben ontvangen (via runtime.onConnect ), zijn uitgeladen.
  • runtime.Port.disconnect() wordt aangeroepen door de andere kant . Als een connect() aanroep resulteert in meerdere poorten aan de kant van de ontvanger, en disconnect() wordt aangeroepen op een van deze poorten, dan wordt de onDisconnect gebeurtenis alleen geactiveerd op de verzendende poort, niet op de andere poorten.

Cross-extension messaging

Naast het versturen van berichten tussen verschillende componenten in uw extensie, kunt u de berichten-API ook gebruiken om met andere extensies te communiceren. Hiermee kunt u een openbare API beschikbaar stellen die andere extensies kunnen gebruiken.

Gebruik de methoden runtime.onMessageExternal of runtime.onConnectExternal om te luisteren naar inkomende verzoeken en verbindingen van andere extensies. Hier is een voorbeeld van elk:

service-worker.js

// For a single request:
chrome.runtime.onMessageExternal.addListener(
  function(request, sender, sendResponse) {
    if (sender.id !== allowlistedExtension) {
      return; // don't allow this extension access
    }
    if (request.getTargetData) {
      sendResponse({ targetData: targetData });
    } else if (request.activateLasers) {
      const 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.
  });
});

Om een ​​bericht naar een andere extensie te sturen, geeft u de ID van de extensie waarmee u wilt communiceren als volgt door:

service-worker.js

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

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

// For a long-lived connection:
const port = chrome.runtime.connect(laserExtensionId);
port.postMessage(...);

Berichten verzenden vanaf webpagina's

Extensies kunnen ook berichten van webpagina's ontvangen en beantwoorden. Om berichten van een webpagina naar een extensie te sturen, specificeert u in uw manifest.json van welke websites u berichten wilt toestaan ​​met behulp van de manifestsleutel "externally_connectable" . Bijvoorbeeld:

manifest.json

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

Hiermee wordt de berichten-API toegankelijk voor elke pagina die overeenkomt met de URL-patronen die u opgeeft. Het URL-patroon moet ten minste een domein op het tweede niveau bevatten; hostnaampatronen zoals "*", "*.com", "*.co.uk" en "*.appspot.com" worden niet ondersteund. U kunt <all_urls> gebruiken om toegang te krijgen tot alle domeinen.

Gebruik de API's runtime.sendMessage() of runtime.connect() om een ​​bericht naar een specifieke extensie te sturen. Bijvoorbeeld:

webpagina.js

// The ID of the extension we want to talk to.
const editorExtensionId = 'abcdefghijklmnoabcdefhijklmnoabc';

// Check if extension is installed
if (chrome && chrome.runtime) {
  // Make a request:
  chrome.runtime.sendMessage(
    editorExtensionId,
    {
      openUrlInEditor: url
    },
    (response) => {
      if (!response.success) handleError(url);
    }
  );
}

Luister vanuit uw extensie naar berichten van webpagina's met behulp van de API's runtime.onMessageExternal of runtime.onConnectExternal , zoals bij cross-extension messaging . Hier is een voorbeeld:

service-worker.js

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

Het is niet mogelijk om een ​​bericht van een extensie naar een webpagina te sturen.

Native berichten

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

Veiligheidsoverwegingen

Hieronder staan ​​enkele beveiligingsoverwegingen met betrekking tot berichten.

Inhoudsscripts zijn minder betrouwbaar

Contentscripts zijn minder betrouwbaar dan de extensieserviceworker. Een kwaadaardige webpagina kan bijvoorbeeld het weergaveproces dat de contentscripts uitvoert, in gevaar brengen. Ga ervan uit dat berichten van een contentscript mogelijk door een aanvaller zijn opgesteld en zorg ervoor dat alle invoer wordt gevalideerd en opgeschoond . Ga ervan uit dat gegevens die naar het contentscript worden verzonden, mogelijk naar de webpagina lekken. Beperk de reikwijdte van geprivilegieerde acties die kunnen worden geactiveerd door berichten die van contentscripts worden ontvangen.

Cross-site scripting

Zorg ervoor dat u uw scripts beschermt tegen cross-site scripting . Wanneer u gegevens ontvangt van een niet-vertrouwde bron, zoals gebruikersinvoer, andere websites via een contentscript of een API, zorg er dan voor dat u deze niet als HTML interpreteert of op een manier gebruikt die onverwachte code kan laten draaien.

Veiligere methoden

Gebruik waar mogelijk API's die geen scripts uitvoeren:

service-worker.js

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // JSON.parse doesn't evaluate the attacker's scripts.
  const resp = JSON.parse(response.farewell);
});

service-worker.js

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // innerText does not let the attacker inject HTML elements.
  document.getElementById("resp").innerText = response.farewell;
});
Onveilige methoden

Vermijd het gebruik van de volgende methoden die uw extensie kwetsbaar maken:

service-worker.js

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // WARNING! Might be evaluating a malicious script!
  const resp = eval(`(${response.farewell})`);
});

service-worker.js

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