Transmissão de mensagens

Como os scripts de conteúdo são executados no contexto de uma página da Web e não na extensão, eles precisam de alguma de se comunicar com o restante da extensão. Por exemplo, uma extensão de leitor de RSS pode usar scripts de conteúdo para detectar a presença de um feed RSS em uma página e notificar a página em segundo plano na para exibir um ícone de ação da página.

A comunicação entre extensões e scripts de conteúdo funciona usando a transmissão de mensagens. De qualquer pode ouvir as mensagens enviadas da outra parte e responder no mesmo canal. Uma mensagem pode conter qualquer objeto JSON válido (nulo, booleano, número, string, matriz ou objeto). Existe um API para solicitações únicas e uma API mais complexa, que permite ter uma experiência de longa duração conexões para trocar várias mensagens em um contexto compartilhado. Também é possível enviar uma mensagem para outra extensão se você souber seu ID, que é abordado no modelo de extensão mensagens.

Solicitações simples únicas

Se você só precisar enviar uma única mensagem para outra parte da sua extensão (e, opcionalmente, receber uma resposta), use as opções simplificadas runtime.sendMessage ou tabs.sendMessage . Isso permite que você envie uma mensagem única serializável em JSON de um script de conteúdo para extensão ou vice-versa versa, respectivamente . Um parâmetro de callback opcional permite lidar com a resposta do outro lado, se houver.

O envio de uma solicitação de um script de conteúdo é semelhante ao seguinte:

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

O envio de uma solicitação da extensão para um script de conteúdo é muito semelhante, exceto pelo fato de que você precisa especificar para qual guia enviá-lo. Este exemplo demonstra o envio de uma mensagem para o script de conteúdo na guia selecionada.

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

Na extremidade de recebimento, você precisa configurar um listener de eventos runtime.onMessage para lidar com os mensagem. A aparência é a mesma de um script de conteúdo ou de uma página de extensão.

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

No exemplo acima, sendResponse foi chamado de forma síncrona. Se você quiser usar de forma assíncrona sendResponse, adicione return true; ao manipulador de eventos onMessage.

Observação: se várias páginas estiverem detectando eventos onMessage, apenas a primeira a chamar sendResponse() para um determinado evento terá êxito no envio da resposta. Todas as outras respostas a esse evento serão ignoradas.
Observação:o callback sendResponse só será válido se usado de forma síncrona ou se o manipulador de eventos retornar true para indicar que vai responder de forma assíncrona. O callback da função sendMessage será invocado automaticamente se nenhum gerenciador retornar verdadeiro ou se o callback sendResponse for coletado da lixeira.

Conexões de longa duração

Às vezes, é útil ter uma conversa que dure mais do que uma única solicitação e resposta. Nesse caso, você pode abrir um canal de longa duração do script de conteúdo para uma página de extensão ou vice-versa, usando runtime.connect ou tabs.connect, respectivamente . O canal pode podem ter um nome, o que permite distinguir entre diferentes tipos de conexões.

Um caso de uso pode ser uma extensão automática de preenchimento de formulário. O script de conteúdo pode abrir um canal para a página da extensão para um login específico e enviar uma mensagem à extensão para cada entrada na página para solicitar os dados do formulário a serem preenchidos. A conexão compartilhada permite que a extensão para manter o estado compartilhado vinculando as várias mensagens provenientes do script de conteúdo.

Ao estabelecer uma conexão, cada extremidade recebe um objeto runtime.Port, que é usado para enviar e receber mensagens por essa conexão.

Veja como abrir um canal usando um script de conteúdo para enviar e detectar mensagens:

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

O envio de uma solicitação da extensão para um script de conteúdo é muito semelhante, exceto pelo fato de que você precisa especifique com qual guia se conectar. Basta substituir a chamada para se conectar no exemplo acima por tabs.connect.

Para gerenciar as conexões de entrada, você precisa configurar um evento runtime.onConnect ouvinte. Ele tem a mesma aparência de um script de conteúdo ou de uma página de extensão. Quando outra parte da sua chama "connect()", esse evento é disparado junto com o objeto runtime.Port que você pode usará para enviar e receber mensagens pela conexão. Veja como é responder a conexões de entrada:

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

Duração da porta

As portas são projetadas como um método de comunicação bidirecional entre diferentes partes da extensão, onde um frame (de nível superior) é visto como a menor parte. Ao chamar tabs.connect, runtime.connect ou runtime.connectNative, uma Port é criada. Essa porta pode ser usada imediatamente para enviar mensagens para a outra extremidade pelo postMessage

Se houver vários frames em uma guia, chamar tabs.connect resultará em diversas invocações de o evento runtime.onConnect (uma vez para cada frame na guia). Da mesma forma, se runtime.connect é usado, o evento onConnect pode ser disparado diversas vezes (uma vez para cada no processo de extensão).

Você pode querer descobrir quando uma conexão é encerrada, por exemplo, se você mantém o estado de cada porta aberta. Para isso, você pode detectar o evento runtime.Port.onDisconnect. Isso é disparado quando não há portas válidas no outro lado do canal. Isso acontece seguintes situações:

  • Não há listeners para runtime.onConnect na outra extremidade.
  • A guia que contém a porta é descarregada (por exemplo, se ela for navegada).
  • O frame de onde connect foi chamado foi descarregado.
  • Todos os frames que receberam a porta (via runtime.onConnect) foram descarregados.
  • runtime.Port.disconnect é chamado pela outra extremidade. Se uma chamada connect resultar em várias portas na extremidade do receptor, e disconnect() é chamado em qualquer uma dessas portas, o evento onDisconnect só é disparado na porta do remetente, e não nas outras.

Mensagens entre extensões

Além de enviar mensagens entre diferentes componentes da sua extensão, você pode usar o a API Messaging para comunicação com outras extensões. Isso permite expor uma API pública que outros que as extensões podem aproveitar.

A detecção de solicitações e conexões recebidas é semelhante ao caso interno, exceto pelo fato de que você usa as métodos runtime.onMessageExternal ou runtime.onConnectExternal. Aqui está um exemplo cada:

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

Da mesma forma, enviar uma mensagem para outra extensão é como enviar uma mensagem dentro da sua extensão. A única diferença é que você precisa transmitir o ID da extensão com a qual deseja se comunicar. Por exemplo:

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

Como enviar mensagens de páginas da Web

Assim como as mensagens entre extensões, seu app ou extensão pode receber e responder a mensagens de páginas da Web comuns. Para usar esse recurso, é necessário primeiro especificar em manifest.json com quais sites você quer se comunicar. Exemplo:

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

Isso vai expor a API de mensagens a qualquer página que corresponda aos padrões de URL especificados. O URL O padrão precisa conter pelo menos um domínio de segundo nível, ou seja, padrões de nome do host como "*", "*.com", "*.co.uk" e "*.appspot.com" são proibidos. Na página da Web, use o runtime.sendMessage ou runtime.connect APIs para enviar uma mensagem a um aplicativo específico ou . Exemplo:

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

No seu app ou extensão, você pode ouvir mensagens de páginas da Web pelo APIs runtime.onMessageExternal ou runtime.onConnectExternal, semelhante a entre extensões mensagens. Somente a página da Web pode iniciar uma conexão. Exemplo:

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

Mensagens nativas

Extensões e aplicativos podem trocar mensagens com aplicativos nativos registrados como um host de mensagens nativas. Para saber mais sobre esse recurso, consulte Mensagens nativas.

Considerações sobre segurança

Scripts de conteúdo são menos confiáveis

Os scripts de conteúdo são menos confiáveis do que a página de fundo da extensão (por exemplo, uma página da Web maliciosa pode comprometer o processo do renderizador em que os scripts de conteúdo são executados). Suponha que mensagens de um script de conteúdo podem ter sido criadas por um invasor e certifique-se de validar e limpar todas as entradas. Suponha que qualquer dado enviado ao script de conteúdo possa vazar para a página da Web. Limitar o escopo de ações privilegiadas que podem ser acionadas por mensagens recebidas do conteúdo scripts.

Scripting em vários locais

Ao receber uma mensagem de um script de conteúdo ou de outra extensão, seus scripts devem ter cuidado a não ser vítima de scripting em vários sites (em inglês). Esse conselho se aplica aos scripts executados página de plano de fundo da extensão, bem como scripts de conteúdo em execução dentro de outras origens da Web. Especificamente, evite usar APIs perigosas, como as seguintes:

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

Em vez disso, dê preferência a APIs mais seguras que não executem 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;
});

Exemplos

Você encontra exemplos simples de comunicação por mensagens em examples/api/messaging diretório. O exemplo de mensagem nativa demonstra como um app do Chrome pode se comunicar com uma app nativo. Para mais exemplos e receber ajuda com a visualização do código-fonte, consulte Amostras.