Transmisión de mensajes

Debido a que las secuencias de comandos de contenido se ejecutan en el contexto de una página web y no en la extensión, a menudo necesitan algunas forma de comunicarse con el resto de la extensión. Por ejemplo, una extensión del lector de RSS podría usar secuencias de comandos de contenido para detectar la presencia de un feed RSS en una página y, luego, notificar a la página en segundo plano en para mostrar un ícono de acción de página para esa página.

La comunicación entre las extensiones y sus secuencias de comandos de contenido funciona con el paso de mensajes. Cualquiera de las siguientes opciones puede escuchar los mensajes enviados desde la otra parte y responder en el mismo canal. Un mensaje puede contener cualquier objeto JSON válido (nulo, booleano, número, string, objeto o matriz). Hay una forma sencilla API para solicitudes únicas y una API más compleja que te permite tener una API de larga duración conexiones para intercambiar múltiples mensajes con un contexto compartido. También es posible enviar un mensaje a otra extensión si conoces su ID. Esto se describe en la sección sobre mensajes.

Solicitudes simples y únicas

Si solo necesitas enviar un mensaje a otra parte de tu extensión (y, opcionalmente, obtener una debes usar runtime.sendMessage o tabs.sendMessage simplificado . Esto te permite enviar un único mensaje JSON serializable desde una secuencia de comandos de contenido a una extensión o viceversa. versa, respectivamente . Un parámetro de devolución de llamada opcional te permite manejar la respuesta de la otra lado, si es que hay uno.

El envío de una solicitud desde una secuencia de comandos de contenido se ve de la siguiente manera:

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

Enviar una solicitud desde la extensión a una secuencia de comandos de contenido es muy similar, excepto que debes especificar a qué pestaña enviarlo. Este ejemplo demuestra cómo enviar un mensaje a la secuencia de comandos de contenido. en la pestaña seleccionada.

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

Debes configurar un objeto de escucha de eventos runtime.onMessage para controlar la mensaje. Esto se ve igual desde una secuencia de comandos de contenido o una página de extensión.

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

En el ejemplo anterior, se llamó a sendResponse de forma síncrona. Si quieres usar de forma asíncrona sendResponse, agrega return true; al controlador de eventos onMessage.

Nota: Si varias páginas escuchan eventos onMessage, solo la primera que llame a sendResponse() para un evento en particular podrá enviar la respuesta. Se ignorarán todas las demás respuestas a ese evento.
Nota: La devolución de llamada sendResponse solo es válida si se usa de forma síncrona o si el controlador de eventos muestra true para indicar que responderá de forma asíncrona. La devolución de llamada de la función sendMessage se invocará automáticamente si ningún controlador muestra el valor true o si la devolución de llamada sendResponse se recolecta como elemento no utilizado.

Conexiones de larga duración

A veces, es útil tener una conversación que dure más que una sola solicitud y respuesta. En este caso, puedes abrir un canal de larga duración desde tu secuencia de comandos de contenido hasta una página de extensión. viceversa, con runtime.connect o tabs.connect, respectivamente . El canal puede tienen un nombre opcional, lo que te permite distinguir entre los diferentes tipos de conexiones.

Un caso de uso podría ser una extensión automática para completar formularios. El guion de contenido podría abrir un canal para la página de la extensión para un acceso específico y enviar un mensaje a la extensión por cada entrada en la página para solicitar que se completen los datos del formulario. La conexión compartida permite que la extensión para mantener el estado compartido vinculando los diversos mensajes provenientes de la secuencia de comandos de contenido.

Al establecer una conexión, a cada extremo se le asigna un objeto runtime.Port que se usa para enviar y recibir mensajes a través de esa conexión.

Sigue estos pasos para abrir un canal desde un guion de contenido y enviar y escuchar mensajes:

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

Enviar una solicitud desde la extensión a una secuencia de comandos de contenido es muy similar, excepto que debes especificar a qué pestaña conectarse. Simplemente reemplaza la llamada para conectar en el ejemplo anterior por tabs.connect.

Para controlar las conexiones entrantes, debes configurar un evento runtime.onConnect objeto de escucha. Esto se ve igual desde una secuencia de comandos de contenido o una página de extensión. Cuando otra parte de tu la extensión llama a "connect()", este evento se activa, junto con el objeto runtime.Port que puedes usar para enviar y recibir mensajes a través de la conexión. A continuación, te mostramos cómo se responde a conexiones 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."});
  });
});

Vida útil del puerto

Los puertos están diseñados como un método de comunicación bidireccional entre diferentes partes de la extensión, en el que un marco (de nivel superior) se ve como la parte más pequeña. Al llamar a tabs.connect, runtime.connect o runtime.connectNative, un puerto cuando se cree. Este puerto puede utilizarse inmediatamente para enviar mensajes al otro extremo a través de postMessage

Si hay varios marcos en una pestaña, llamar a tabs.connect genera varias invocaciones de el evento runtime.onConnect (una vez para cada fotograma en la pestaña) Del mismo modo, si runtime.connect, entonces el evento onConnect se puede activar varias veces (una vez por cada marco en el proceso de extensión).

Es posible que quieras saber cuándo se cierra una conexión, por ejemplo, si mantienes separados para cada puerto abierto. Para esto, puedes escuchar el evento runtime.Port.onDisconnect. Esta se activa cuando no hay puertos válidos en el otro lado del canal. Esto sucede las siguientes situaciones:

  • No hay objetos de escucha para runtime.onConnect en el otro extremo.
  • La pestaña que contiene el puerto está descargada (p.ej., si se navega por la pestaña).
  • Se descargó el fotograma desde el que se llamó a connect.
  • Se descargaron todas las tramas que recibieron el puerto (a través de runtime.onConnect).
  • el otro extremo llama a runtime.Port.disconnect. Ten en cuenta que si una llamada connect genera en varios puertos en el extremo del receptor y se llama a disconnect() en cualquiera de estos puertos, El evento onDisconnect solo se activa en el puerto del remitente y no en los otros puertos.

Mensajería entre extensiones

Además de enviar mensajes entre diferentes componentes de tu extensión, puedes usar el Messaging para comunicarse con otras extensiones. Esto te permite exponer una API pública que otras que pueden aprovechar las extensiones.

Escuchar solicitudes y conexiones entrantes es similar al caso interno, excepto que se usa runtime.onMessageExternal o runtime.onConnectExternal. Aquí hay un ejemplo de cada uno:

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

Del mismo modo, enviar un mensaje a otra extensión es similar a enviar uno dentro de tu extensión. La única diferencia es que debes pasar el ID de la extensión con la que deseas comunicarte. Por ejemplo:

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

Cómo enviar mensajes desde páginas web

Al igual que los mensajes entre extensiones, su aplicación o extensión puede recibir y responder mensajes de páginas web normales. Para utilizar esta función, primero debes especificar el archivo en tu archivo manifest.json con qué sitios web deseas comunicarte. Por ejemplo:

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

Esto expondrá la API de mensajería en cualquier página que coincida con los patrones de URL que especifiques. La URL debe contener, al menos, un dominio de segundo nivel, es decir, patrones de nombre de host como "*", "*.com", "*.co.uk" y "*.appspot.com" están prohibidas. Desde la página web, usa la APIs de runtime.sendMessage o runtime.connect para enviar un mensaje a una app específica . Por ejemplo:

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

Desde tu app o extensión, puedes escuchar mensajes de páginas web a través de la APIs de runtime.onMessageExternal o runtime.onConnectExternal, similares a las extensiones cruzadas mensajes de texto. Solo la página web puede iniciar una conexión. A continuación, se muestra un ejemplo:

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

Mensajería nativa

Las extensiones y apps pueden intercambiar mensajes con aplicaciones nativas que están registradas como host de mensajería nativa. Para obtener más información sobre esta función, consulta el artículo Mensajería nativa.

Consideraciones de seguridad

Las secuencias de comandos de contenido son menos confiables

Las secuencias de comandos de contenido son menos confiables que la página en segundo plano de la extensión (p.ej., una página web maliciosa esta página pueda comprometer el proceso del renderizador en el que se ejecutan las secuencias de comandos de contenido). Supongamos que mensajes de una secuencia de comandos de contenido podrían haber sido elaborados por un atacante. Asegúrate de validar y limpiar todas las entradas. Supongamos que todos los datos enviados a la secuencia de comandos de contenido pueden filtrarse en la página web. Limitar el alcance de las acciones con privilegios que pueden activarse con mensajes recibidos de contenido secuencias de comandos.

Secuencia de comandos entre sitios

Cuando recibas un mensaje de una secuencia de comandos de contenido u otra extensión, tus secuencias de comandos deben tener cuidado. no ser víctima de secuencias de comandos entre sitios. Este consejo se aplica a las secuencias de comandos que se ejecutan página de fondo de la extensión, así como a las secuencias de comandos de contenido que se ejecutan en otros orígenes web. Específicamente, evita usar APIs peligrosas, como las que se muestran a continuación:

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

En su lugar, usa APIs más seguras que no ejecuten secuencias de comandos:

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

Ejemplos

Puedes encontrar ejemplos sencillos de comunicación a través de mensajes en examples/api/messaging . El ejemplo de mensajería nativa demuestra cómo una app de Chrome puede comunicarse con un app nativa. Si deseas obtener más ejemplos y ayuda para ver el código fuente, consulta Muestras.