Message transmis

Étant donné que les scripts de contenu s'exécutent dans le contexte d'une page Web et non dans l'extension, ils ont souvent besoin moyen de communication avec le reste de l'extension. Par exemple, une extension de lecteur RSS peut utiliser des scripts de contenu pour détecter la présence d'un flux RSS sur une page, puis notifier la page en arrière-plan dans afin d'afficher une icône d'action pour cette page.

La communication entre les extensions et leurs scripts de contenu repose sur la transmission de messages. L'un ou l'autre peut écouter les messages envoyés de l’autre extrémité et y répondre sur le même canal. Un message peut contenir un objet JSON valide (null, booléen, number, chaîne, tableau ou objet). Il existe un pour les requêtes ponctuelles et une API plus complexe qui vous permet d'avoir des requêtes de longue durée des contacts pour échanger plusieurs messages dans un contexte partagé. Il est également possible d'envoyer un message à une autre extension si vous connaissez son ID, qui est couvert par l'instruction cross-extension messages.

Requêtes ponctuelles simples

Si vous n'avez besoin d'envoyer qu'un seul message à une autre partie de l'extension (et si vous souhaitez également recevoir réponse), utilisez la méthode simplifiée runtime.sendMessage ou tabs.sendMessage . Cela vous permet d'envoyer un message unique sérialisable au format JSON à partir d'un script de contenu vers l'extension , ou vice versa. versa, respectivement . Un paramètre de rappel facultatif vous permet de gérer la réponse de l'autre s'il y en a une.

Voici à quoi ressemble l'envoi d'une requête à partir d'un script de contenu:

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

Le processus d'envoi d'une requête à un script de contenu depuis l'extension est très semblable, si ce n'est que vous devez spécifier l'onglet vers lequel l'envoyer. Cet exemple illustre l'envoi d'un message au script de contenu dans l'onglet sélectionné.

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

Du côté de la réception, vous devez configurer un écouteur d'événements runtime.onMessage pour gérer le . Il en va de même pour un script de contenu ou une page d'extension.

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

Dans l'exemple ci-dessus, sendResponse a été appelé de manière synchrone. Si vous souhaitez utiliser de manière asynchrone sendResponse, ajoutez return true; au gestionnaire d'événements onMessage.

Remarque:Si plusieurs pages écoutent des événements onMessage, seule la première à appeler sendResponse() pour un événement particulier pourra envoyer la réponse. Toutes les autres réponses à cet événement seront ignorées.
Remarque:Le rappel sendResponse n'est valide que s'il est utilisé de manière synchrone ou si le gestionnaire d'événements renvoie true pour indiquer qu'il répondra de manière asynchrone. Le rappel de la fonction sendMessage sera appelé automatiquement si aucun gestionnaire ne renvoie la valeur "true" ou si le rappel sendResponse fait l'objet d'une récupération de mémoire.

Connexions de longue durée

Il est parfois utile d'avoir une conversation qui dure plus longtemps qu'une seule demande et une seule réponse. Dans ce cas, vous pouvez ouvrir une chaîne de longue durée depuis votre script de contenu vers une page d'extension , ou utilisez runtime.connect ou tabs.connect, respectivement . La chaîne peut éventuellement un nom, ce qui vous permet de faire la distinction entre les différents types de connexions.

L'extension de remplissage automatique de formulaire peut par exemple être utilisée. Le script de contenu peut ouvrir une chaîne la page de l'extension pour une connexion donnée et envoyer un message à l'extension pour chaque entrée de la page pour demander le remplissage des données du formulaire. La connexion partagée permet à l'extension pour conserver l'état partagé reliant les différents messages provenant du script de contenu.

Lors de l'établissement d'une connexion, chaque extrémité reçoit un objet runtime.Port utilisé pour l'envoi et la réception de messages via cette connexion.

Voici comment ouvrir une chaîne à partir d'un script de contenu, puis envoyer et écouter des messages:

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

Le processus d'envoi d'une requête à un script de contenu depuis l'extension est très semblable, si ce n'est que vous devez spécifier l'onglet auquel se connecter. Remplacez simplement l'appel pour établir la connexion dans l'exemple ci-dessus par tabs.connect.

Pour gérer les connexions entrantes, vous devez configurer un événement runtime.onConnect. l'écouteur. Il en va de même pour un script de contenu ou une page d'extension. Lorsqu'une autre partie de votre l'extension appelle "connect()", cet événement est déclenché, ainsi que l'objet runtime.Port que vous pouvez utiliser pour envoyer et recevoir des messages via la connexion. Voici comment cela se présente lorsque vous répondez à connexions entrantes:

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

Durée de vie des ports

Les ports sont conçus comme une méthode de communication bidirectionnelle entre différentes parties de l'extension, où un cadre (de niveau supérieur) est considéré comme la plus petite partie. Lorsque vous appelez tabs.connect, runtime.connect ou runtime.connectNative, un Port est créé. Ce port peut immédiatement être utilisé pour envoyer des messages à l’autre extrémité via postMessage.

S'il y a plusieurs frames dans un onglet, l'appel de tabs.connect entraîne plusieurs invocations de l'événement runtime.onConnect (une fois pour chaque image de l'onglet). De même, si runtime.connect, l'événement onConnect peut alors être déclenché plusieurs fois (une fois cadre dans le processus d'extension).

Il peut être utile de savoir quand une connexion est fermée, par exemple si vous gérez des pour chaque port ouvert. Pour cela, vous pouvez écouter l'événement runtime.Port.onDisconnect. Ce est déclenché lorsqu'il n'y a pas de ports valides de l'autre côté du canal. Cela se produit dans situations suivantes:

  • Aucun écouteur n'est disponible pour runtime.onConnect à l'autre extrémité.
  • L'onglet contenant le port est déchargé (par exemple, si l'utilisateur navigue dans l'onglet).
  • Le frame à partir duquel connect a été appelé a été déchargé.
  • Toutes les trames ayant reçu le port (via runtime.onConnect) ont été déchargées.
  • runtime.Port.disconnect est appelé par l'autre extrémité. Notez que si un appel connect génère sur plusieurs ports à l'extrémité du récepteur, et que disconnect() est appelé sur l'un de ces ports, l'événement onDisconnect n'est déclenché que sur le port de l'expéditeur, et non sur les autres ports.

Argumentaire pour plusieurs extensions

En plus d'envoyer des messages entre différents composants de votre extension, vous pouvez utiliser de messagerie pour communiquer avec d'autres extensions. Cela vous permet d'exposer une API publique que d'autres dont vos extensions peuvent tirer profit.

L'écoute des requêtes et connexions entrantes est semblable au cas interne, si ce n'est que vous utilisez la classe runtime.onMessageExternal ou runtime.onConnectExternal. Voici un exemple chacun:

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

De la même manière, envoyer un message à une autre extension revient à envoyer un message au sein de votre extension. La seule différence est que vous devez transmettre l'ID de l'extension avec laquelle vous souhaitez communiquer. Exemple :

// 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(...);

Envoyer des messages à partir de pages Web

À l'instar des messages inter-extensions, votre application ou votre extension peut recevoir les messages provenant de pages Web standards. Pour utiliser cette fonctionnalité, vous devez d'abord l'indiquer dans le fichier manifest.json les sites Web avec lesquels vous souhaitez communiquer. Exemple :

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

L'API de messagerie sera ainsi exposée à toute page correspondant aux formats d'URL que vous spécifiez. L'URL Le modèle doit contenir au moins un domaine de deuxième niveau, c'est-à-dire des modèles de nom d'hôte tels que "*", "*.com", "*.co.uk" et "*.appspot.com" sont interdites. Sur la page Web, utilisez la méthode runtime.sendMessage ou runtime.connect pour envoyer un message à une application ou . Exemple :

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

Depuis votre application ou votre extension, vous pouvez écouter les messages de pages Web via le runtime.onMessageExternal ou runtime.onConnectExternal, semblables à cross-extension messagerie. Seule la page Web peut établir une connexion. Voici un exemple :

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

Messagerie native

Les extensions et les applications peuvent échanger des messages avec des applications natives enregistrées en tant que hôte de messagerie native. Pour en savoir plus sur cette fonctionnalité, consultez Messagerie native.

Considérations de sécurité

Les scripts de contenu sont moins fiables

Les scripts de contenu sont moins fiables que la page d'arrière-plan de l'extension (par exemple, une page Web malveillante peut compromettre le processus du moteur de rendu où s'exécutent les scripts de contenu). Partez du principe que les messages d'un script de contenu peuvent avoir été créés par un pirate informatique. Veillez à valider et vérifier Nettoyer toutes les entrées Supposons que toutes les données envoyées au script de contenu puissent fuir vers la page Web. Limiter la portée des actions privilégiées pouvant être déclenchées par les messages provenant du contenu les scripts.

Script intersites

Lorsque vous recevez un message d'un script de contenu ou d'une autre extension, vos scripts doivent faire attention pour éviter d'être victime des scripts intersites. Ce conseil s'applique aux scripts exécutés à la page d'arrière-plan de l'extension, ainsi qu'aux scripts de contenu exécutés au sein d'autres origines Web. Plus précisément, évitez d'utiliser des API dangereuses telles que les suivantes:

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

Optez plutôt pour des API plus sûres qui n'exécutent pas de scripts:

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

Exemples

Vous trouverez des exemples simples de communication via des messages dans examples/api/messaging . L'exemple de messagerie native montre comment une application Chrome peut communiquer avec un application native. Pour obtenir plus d'exemples et obtenir de l'aide concernant l'affichage du code source, consultez la section Exemples.