Messaggio superato

Dato che gli script di contenuti vengono eseguiti nel contesto di una pagina web e non dell'estensione, spesso sono necessarie comunicare con il resto dell'estensione. Ad esempio, un'estensione lettore RSS potrebbe utilizzare script di contenuti per rilevare la presenza di un feed RSS su una pagina e inviare una notifica alla pagina di sfondo in per visualizzare un'icona di azione sulla pagina per quella pagina.

La comunicazione tra le estensioni e i relativi script di contenuti funziona tramite la trasmissione dei messaggi. Entrambi possono ascoltare i messaggi inviati dall'altro capo del telefono e rispondere sullo stesso canale. Un messaggio può contenere qualsiasi oggetto JSON valido (nullo, booleano, numero, stringa, array oppure oggetto). C'è una semplice API per richieste una tantum e un'API più complessa che consente di avere di connessione per lo scambio di più messaggi in un contesto condiviso. È anche possibile inviare un messaggio a un'altra estensione se ne conosci l'ID, come indicato nell'attributo cross-extension messaggi.

Semplici richieste una tantum

Se devi solo inviare un singolo messaggio a un'altra parte dell'estensione (e facoltativamente ricevere un ), devi utilizzare la versione semplificata runtime.sendMessage o tabs.sendMessage . Ciò ti consente di inviare un messaggio singolo con serie JSON da uno script di contenuti all'estensione o al componente rispettivamente . Un parametro di callback facoltativo ti consente di gestire la risposta dall'altro di rete, se presente.

L'invio di una richiesta da uno script di contenuti ha il seguente aspetto:

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

L'invio di una richiesta dall'estensione a uno script di contenuti sembra molto simile, ad eccezione del fatto che è necessario specificare la scheda a cui inviarlo. Questo esempio mostra l'invio di un messaggio allo script dei contenuti nella scheda selezionata.

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

Sul lato ricevente, devi impostare un listener di eventi runtime.onMessage per gestire . Questo sembra lo stesso da una pagina di script di contenuti o di estensione.

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

Nell'esempio precedente, sendResponse è stato chiamato in modo sincrono. Se vuoi utilizzare in modo asincrono sendResponse, aggiungi return true; al gestore di eventi onMessage.

Nota: se più pagine sono in ascolto degli eventi onMessage, solo la prima chiamata a sendResponse() per un determinato evento sarà in grado di inviare la risposta. Tutte le altre risposte a quell'evento verranno ignorate.
Nota: il callback sendResponse è valido solo se utilizzato in modo sincrono o se il gestore di eventi restituisce true per indicare che risponderà in modo asincrono. Il callback della funzione sendMessage verrà richiamato automaticamente se nessun gestore restituisce true o se il callback sendResponse viene eseguito in garbage-collect.

Connessioni di lunga durata

A volte è utile avere una conversazione che dura più di una singola richiesta e risposta. In questo caso, puoi aprire un canale di lunga durata dallo script dei contenuti a una pagina di estensione oppure viceversa, utilizzando rispettivamente runtime.connect o tabs.connect . Il canale può facoltativamente avere un nome, che consente di distinguere tra i diversi tipi di connessione.

Un caso d'uso potrebbe essere un'estensione per la compilazione automatica dei moduli. Lo script dei contenuti potrebbe aprire un canale pagina dell'estensione per un determinato accesso e invia un messaggio all'estensione per ogni input nella pagina per richiedere i dati del modulo da compilare. La connessione condivisa consente all'estensione per mantenere lo stato condiviso che collega i vari messaggi provenienti dallo script dei contenuti.

Quando stabilisci una connessione, a ciascuna estremità viene assegnato un oggetto runtime.Port, utilizzato per l'invio e la ricezione di messaggi attraverso quella connessione.

Ecco come aprire un canale da un copione dei contenuti e inviare e ascoltare i messaggi:

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

L'invio di una richiesta dall'estensione a uno script di contenuti sembra molto simile, ad eccezione del fatto che è necessario specificare a quale scheda connettersi. Devi solo sostituire la chiamata per connetterti nell'esempio precedente con tabs.connect.

Per gestire le connessioni in entrata, devi configurare un evento runtime.onConnect. e ascolto. Questo sembra lo stesso da uno script di contenuti o da una pagina di estensione. Quando un'altra parte l'estensione chiama "connect()", ma viene attivato questo evento, insieme all'oggetto runtime.Port che puoi per inviare e ricevere messaggi tramite la connessione. Ecco come funziona la risposta connessioni in entrata:

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

Durata della porta

Le porte sono progettate come metodo di comunicazione bidirezionale tra le diverse parti dell'estensione, dove un frame di primo livello è visto come la parte più piccola. Quando chiami tabs.connect, runtime.connect o runtime.connectNative, una porta viene creato. Questa porta può essere utilizzata immediatamente per inviare messaggi all'altro capo del telefono tramite postMessage.

Se in una scheda sono presenti più frame, la chiamata a tabs.connect comporta più chiamate di l'evento runtime.onConnect (una volta per ogni frame nella scheda). Analogamente, se Se viene utilizzato runtime.connect, l'evento onConnect può essere attivato più volte (una volta per ogni nel processo di estensione).

Potresti voler sapere quando viene chiusa una connessione, ad esempio se mantieni un per ogni porta aperta. Per questo, puoi ascoltare l'evento runtime.Port.onDisconnect. Questo viene attivato quando non ci sono porte valide sull'altro lato del canale. Ciò si verifica le seguenti situazioni:

  • Non ci sono listener per runtime.onConnect all'altra estremità.
  • La scheda contenente la porta viene scaricata (ad esempio se viene navigata nella scheda).
  • Il frame da cui è stato chiamato connect è stato scaricato.
  • Tutti i frame che hanno ricevuto la porta (tramite runtime.onConnect) sono stati scaricati.
  • runtime.Port.disconnect viene chiamato dall'altra estremità. Tieni presente che se viene generata una chiamata connect in più porte all'estremità del ricevitore e disconnect() viene chiamato su una di queste porte, quindi l'evento onDisconnect viene attivato solo alla porta del mittente e non alle altre porte.

Messaggistica con estensioni diverse

Oltre a inviare messaggi tra diversi componenti dell'estensione, puoi utilizzare API di messaggistica per comunicare con altre estensioni. Ciò ti consente di esporre un'API pubblica che che le estensioni possono sfruttare.

L'ascolto di richieste e connessioni in arrivo è simile alla richiesta interna, ad eccezione del fatto che utilizzi runtime.onMessageExternal o runtime.onConnectExternal. Ecco un esempio di ognuno:

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

Analogamente, l'invio di un messaggio a un'altra estensione è simile all'invio di un messaggio all'interno dell'estensione. L'unica differenza è che devi trasmettere l'ID dell'estensione con cui vuoi comunicare. Ad esempio:

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

Invio di messaggi da pagine web

Analogamente ai messaggi con estensioni incrociate, la tua app o estensione può ricevere messaggi e rispondere messaggi da pagine web standard. Per utilizzare questa funzionalità, devi prima specificare nel file manifest.json con quali siti web desideri comunicare. Ad esempio:

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

In questo modo l'API di messaggistica verrà mostrata in tutte le pagine che corrispondono ai pattern URL specificati. URL deve contenere almeno un dominio di secondo livello, ovvero pattern di nomi host come "*", "*.com", "*.co.uk" e "*.appspot.com" sono vietati. Dalla pagina web, utilizza API runtime.sendMessage o runtime.connect per inviare un messaggio a un'app specifica o . Ad esempio:

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

Dalla tua app o estensione, puoi ascoltare i messaggi dalle pagine web tramite la API runtime.onMessageExternal o runtime.onConnectExternal, simili a cross-extension di messaggistica. Solo la pagina web può avviare una connessione. Ecco un esempio:

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

Messaggistica nativa

Le estensioni e le applicazioni possono scambiare messaggi con applicazioni native registrate come host di messaggistica nativo. Per saperne di più su questa funzionalità, consulta Messaggistica nativa.

Considerazioni sulla sicurezza

I testi dei contenuti sono meno affidabili

Gli script di contenuti sono meno affidabili della pagina di sfondo delle estensioni (ad es. un server web dannoso potrebbe compromettere il processo del renderer in cui vengono eseguiti gli script dei contenuti. Supponiamo che messaggi da uno script di contenuti potrebbero essere stati creati da un utente malintenzionato e assicurarsi di convalidare sanifica tutti gli input. Supponi che i dati inviati allo script di contenuti possano essere divulgati alla pagina web. Limita l'ambito delle azioni privilegiate che possono essere attivate dai messaggi ricevuti dai contenuti script.

Cross-site scripting (XSS)

Quando ricevi un messaggio da uno script di contenuti o da un'altra estensione, gli script devono fare attenzione non cadere vittima di cross-site scripting. Questo consiglio si applica agli script eseguiti all'interno sfondo con estensione, oltre che agli script di contenuti in esecuzione all'interno di altre origini web. In particolare, evita di utilizzare API pericolose come quelle riportate di seguito:

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

Preferisci API più sicure che non eseguono script:

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

Esempi

Puoi trovare semplici esempi di comunicazione tramite messaggi in examples/api/messaggiging . L'esempio di messaggistica nativa mostra come un'app di Chrome può comunicare con un nativa. Per altri esempi e per assistenza nella visualizzazione del codice sorgente, consulta Esempi.