Scripts de conteúdo

Scripts de conteúdo são arquivos executados no contexto de páginas da Web. Com o modelo de objeto de documentos (DOM) padrão, eles podem ler detalhes das páginas da Web que o navegador visita, fazer alterações nelas e transmitir informações para a extensão pai.

Entender os recursos do script de conteúdo

Os scripts de conteúdo podem acessar as APIs do Chrome usadas pela extensão mãe, trocando mensagens com a extensão. Eles também podem acessar o URL do arquivo de uma extensão com chrome.runtime.getURL() e usar o resultado da mesma forma que outros URLs.

// Code for displaying EXTENSION_DIR/images/myimage.png:
var imgURL = chrome.runtime.getURL("images/myimage.png");
document.getElementById("someImage").src = imgURL;

Além disso, o content script pode acessar as seguintes APIs do Chrome diretamente:

Os scripts de conteúdo não conseguem acessar outras APIs diretamente.

Trabalhe em mundos isolados

Os scripts de conteúdo residem em um mundo isolado, permitindo que um script de conteúdo faça alterações no ambiente JavaScript sem entrar em conflito com a página ou com os scripts de conteúdo adicionais.

Uma extensão pode ser executada em uma página da Web com código semelhante ao exemplo abaixo.

<html>
  <button id="mybutton">click me</button>
  <script>
    var greeting = "hello, ";
    var button = document.getElementById("mybutton");
    button.person_name = "Bob";
    button.addEventListener("click", function() {
      alert(greeting + button.person_name + ".");
    }, false);
  </script>
</html>

Essa extensão pode injetar o script de conteúdo a seguir.

var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener("click", function() {
  alert(greeting + button.person_name + ".");
}, false);

Os dois alertas apareceriam se o botão fosse pressionado.

Os mundos isolados não permitem que scripts de conteúdo, a extensão e a página da Web acessem quaisquer variáveis ou funções criadas por outros. Isso também permite que os scripts de conteúdo ativem funcionalidades que não podem ser acessadas pela página da Web.

Injetar scripts

Os scripts de conteúdo podem ser injetados de maneira programativa ou declarativa.

Injetar programaticamente

Use a injeção programática para scripts de conteúdo que precisam ser executados em ocasiões específicas.

Para injetar um script de conteúdo programático, conceda a permissão activeTab no manifesto. Isso concede acesso seguro ao host do site ativo e acesso temporário à permissão tabs, permitindo que o script de conteúdo seja executado na guia ativa atual sem especificar permissões de origem cruzada.

{
  "name": "My extension",
  ...
  "permissions": [
    "activeTab"
  ],
  ...
}

Os scripts de conteúdo podem ser injetados como código.

chrome.runtime.onMessage.addListener(
  function(message, callback) {
    if (message == "changeColor"){
      chrome.tabs.executeScript({
        code: 'document.body.style.backgroundColor="orange"'
      });
    }
  });

Também é possível injetar um arquivo inteiro.

chrome.runtime.onMessage.addListener(
  function(message, callback) {
    if (message == "runContentScript"){
      chrome.tabs.executeScript({
        file: 'contentScript.js'
      });
    }
  });

Injetar de forma declarativa

Use a injeção declarativa para scripts de conteúdo que devem ser executados automaticamente em páginas especificadas.

Os scripts injetados de forma declarativa são registrados no manifesto no campo "content_scripts". Eles podem incluir arquivos JavaScript, CSS ou ambos. Todos os scripts de conteúdo executados automaticamente precisam especificar padrões de correspondência.

{
 "name": "My extension",
 ...
 "content_scripts": [
   {
     "matches": ["http://*.nytimes.com/*"],
     "css": ["myStyles.css"],
     "js": ["contentScript.js"]
   }
 ],
 ...
}
Nome Tipo Descrição
matches {: #matches } matriz de strings Obrigatório. Especifica em quais páginas este script de conteúdo será injetado. Consulte Padrões de correspondência para mais detalhes sobre a sintaxe dessas strings e Padrões de correspondência e globs para informações sobre como excluir URLs.
css {: #css } matriz de strings Opcional. Lista de arquivos CSS a serem injetados nas páginas correspondentes. Eles são injetados na ordem em que aparecem nessa matriz, antes de qualquer DOM ser construído ou exibido para a página.
js {: #js } matriz de strings Opcional. Lista de arquivos JavaScript a serem injetados nas páginas correspondentes. Eles são injetados na ordem em que aparecem nesta matriz.
match_about_blank {: #match_about_blank } boolean Opcional. Indica se o script precisa ser injetado em um frame about:blank em que o frame pai ou aberto corresponde a um dos padrões declarados em matches. O padrão é false.

Excluir correspondências e globs

A correspondência especificada da página é personalizável incluindo os seguintes campos no registro do manifesto.

Nome Tipo Descrição
exclude_matches {: #excluded_matches } matriz de strings Opcional. Exclui páginas em que esse script de conteúdo seria injetado de outra forma. Consulte Padrões de correspondência para mais detalhes sobre a sintaxe dessas strings.
include_globs {: #include_globs } matriz de strings Opcional. Aplicado após matches para incluir apenas os URLs que também correspondem a este glob. Destina-se a emular a palavra-chave @include Greasemonkey.
exclude_globs {: #exclude_globs } matriz de strings Opcional. Aplicado após matches para excluir URLs que correspondem a este glob. Destina-se a emular a palavra-chave @excludeGreasemonkey.

O script de conteúdo vai ser injetado em uma página se o URL dela corresponder a qualquer padrão matches e include_globs, desde que o URL também não corresponda a um padrão exclude_matches ou exclude_globs.

Como a propriedade matches é obrigatória, exclude_matches, include_globs e exclude_globs só podem ser usados para limitar quais páginas serão afetadas.

A extensão a seguir injetaria o script de conteúdo na http://www.nytimes.com/ health, mas não na http://www.nytimes.com/ business .

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "exclude_matches": ["*://*/*business*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

As propriedades glob seguem uma sintaxe diferente e mais flexível do que os padrões de correspondência. Strings glob aceitáveis são URLs que podem conter asteriscos "caracteres curinga" e pontos de interrogação. O asterisco * corresponde a qualquer string de qualquer tamanho, incluindo a string vazia, enquanto o ponto de interrogação ? corresponde a um único caractere.

Por exemplo, o glob http:// ??? .example.com/foo/ * corresponde a qualquer um dos seguintes:

  • http:// www .example.com/foo /bar
  • http:// de .example.com/foo /

No entanto, ele não corresponde ao seguinte:

  • http:// meu .example.com/foo/bar
  • http:// example .com/foo/
  • http://www.example.com/foo

Essa extensão injetaria o script de conteúdo em http:/www.nytimes.com/ Arts /index.html e http://www.nytimes.com/ jobs /index.html, mas não em http://www.nytimes.com/ esportes /index.html.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "include_globs": ["*nytimes.com/???s/*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

Essa extensão injetaria o script de conteúdo em http:// history .nytimes.com e em http://.nytimes.com/ history, mas não em http:// science .nytimes.com ou http://www.nytimes.com/ science .

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "exclude_globs": ["*science*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

É possível incluir apenas um, todos ou alguns deles para atingir o escopo correto.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "exclude_matches": ["*://*/*business*"],
      "include_globs": ["*nytimes.com/???s/*"],
      "exclude_globs": ["*science*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

Ambiente de execução

A injeção de arquivos JavaScript na página da Web é feita pelo campo run_at. O campo preferencial e padrão é "document_idle", mas também pode ser especificado como "document_start" ou "document_end", se necessário.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "run_at": "document_idle",
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Nome Tipo Descrição
document_idle {: #document_idle } string Preferida. Use "document_idle" sempre que possível.

O navegador escolhe um horário para injetar scripts entre "document_end" e imediatamente após o disparo do evento windowonload. O momento exato da injeção depende da complexidade do documento e do tempo que ele leva para carregar. Além disso, ele é otimizado para a velocidade de carregamento da página.

Os scripts de conteúdo em execução em "document_idle" não precisam detectar o evento window.onload, porque eles vão ser executados após a conclusão do DOM. Se um script realmente precisa ser executado depois de window.onload, a extensão pode verificar se onload já foi disparado usando a propriedade document.readyState.
document_start {: #document_start } string Os scripts são injetados depois de todos os arquivos de css, mas antes de qualquer outro DOM ser construído ou de qualquer outro script ser executado.
document_end {: #document_end } string Os scripts são injetados imediatamente após a conclusão do DOM, mas antes do carregamento de sub-recursos, como imagens e frames.

Especificar frames

O campo "all_frames" permite que a extensão especifique se arquivos JavaScript e CSS precisam ser injetados em todos os frames que correspondem aos requisitos de URL especificados ou apenas no frame superior de uma guia.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "all_frames": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Nome Tipo Descrição
all_frames {: #all_frames } boolean Opcional. O padrão é false, o que significa que apenas o frame superior é correspondido.

Se for especificado true, ele será injetado em todos os frames, mesmo que não seja o frame superior na guia. Cada frame é verificado de forma independente em relação aos requisitos de URL. Ele não será injetado em frames filhos se os requisitos de URL não forem atendidos.

Comunicação com a página de incorporação

Embora os ambientes de execução dos scripts de conteúdo e as páginas que os hospedam estejam isolados entre si, eles compartilham o acesso ao DOM da página. Se a página quiser se comunicar com o script de conteúdo ou com a extensão pelo script de conteúdo, ela precisará fazer isso pelo DOM compartilhado.

Veja um exemplo usando window.postMessage:

var port = chrome.runtime.connect();

window.addEventListener("message", function(event) {
  // We only accept messages from ourselves
  if (event.source != window)
    return;

  if (event.data.type && (event.data.type == "FROM_PAGE")) {
    console.log("Content script received: " + event.data.text);
    port.postMessage(event.data.text);
  }
}, false);
document.getElementById("theButton").addEventListener("click",
    function() {
  window.postMessage({ type: "FROM_PAGE", text: "Hello from the webpage!" }, "*");
}, false);

A página sem extensão, example.html, posta mensagens para si mesma. Essa mensagem é interceptada e inspecionada pelo script de conteúdo e, em seguida, postada no processo de extensão. Dessa forma, a página estabelece uma linha de comunicação para o processo de extensão. O inverso é possível por meios semelhantes.

Proteja-se

Embora mundos isolados forneçam uma camada de proteção, o uso de scripts de conteúdo pode criar vulnerabilidades em uma extensão e na página da Web. Se o script de conteúdo receber conteúdo de um site separado, como ao fazer um XMLHttpRequest, tenha cuidado para filtrar ataques de scripting em vários sites de conteúdo antes de injetá-lo. Comunique-se somente por HTTPS para evitar ataques "man-in-the-middle".

Filtre as páginas da Web maliciosas. Por exemplo, os seguintes padrões são perigosos:

var data = document.getElementById("json-data")
// WARNING! Might be evaluating an evil script!
var parsed = eval("(" + data + ")")
var elmt_id = ...
// WARNING! elmt_id might be "); ... evil script ... //"!
window.setTimeout("animate(" + elmt_id + ")", 200);

Em vez disso, prefira APIs mais seguras que não executam scripts:

var data = document.getElementById("json-data")
// JSON.parse does not evaluate the attacker's scripts.
var parsed = JSON.parse(data);
var elmt_id = ...
// The closure form of setTimeout does not evaluate scripts.
window.setTimeout(function() {
  animate(elmt_id);
}, 200);