Scripts de conteúdo são arquivos executados no contexto de páginas da Web. Com a função Documento Modelo de objeto (DOM), eles podem ler detalhes das páginas da Web que o navegador visita, fazer alterações feitas neles e passar informações para a extensão principal.
Compreender as capacidades do script de conteúdo
Os scripts de conteúdo podem acessar as seguintes APIs de extensão diretamente:
dom
i18n
storage
runtime.connect()
runtime.getManifest()
runtime.getURL()
runtime.id
runtime.onConnect
runtime.onMessage
runtime.sendMessage()
Os scripts de conteúdo não podem acessar outras APIs diretamente. Mas eles podem acessá-las indiretamente, trocando mensagens com outras partes da sua extensão.
Você também pode acessar outros arquivos em sua extensão a partir de um script de conteúdo, usando
APIs como fetch()
. Para fazer isso, é necessário declará-las como
recursos acessíveis pela Web. Isso também expõe os recursos a qualquer
scripts próprios ou de terceiros em execução no mesmo site.
Trabalhe em mundos isolados
Os scripts de conteúdo vivem em um mundo isolado, permitindo que um script de conteúdo faça alterações em seu JavaScript sem conflito com a página ou outras extensões scripts de conteúdo.
Uma extensão pode ser executada em uma página da Web com código semelhante ao exemplo a seguir.
webPage.html
<html>
<button id="mybutton">click me</button>
<script>
var greeting = "hello, ";
var button = document.getElementById("mybutton");
button.person_name = "Bob";
button.addEventListener(
"click", () => alert(greeting + button.person_name + "."), false);
</script>
</html>
Essa extensão pode injetar o script de conteúdo a seguir usando uma das técnicas descritas na Seção Injetar scripts.
content-script.js
var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener(
"click", () => alert(greeting + button.person_name + "."), false);
Com essa mudança, os dois alertas vão aparecer em sequência quando o botão for clicado.
Injetar scripts
Os scripts de conteúdo podem ser declarados estaticamente, declarados de forma dinâmica ou injetada de forma programática.
Injetar com declarações estáticas
Use declarações de script de conteúdo estático no manifest.json para scripts que serão automaticamente são executados em um conjunto de páginas bem conhecido.
Os scripts declarados estaticamente são registrados no manifesto na chave "content_scripts"
.
Eles podem incluir arquivos JavaScript, CSS ou ambos. Todos os scripts de conteúdo de execução automática precisam especificar
padrões de correspondência.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"css": ["my-styles.css"],
"js": ["content-script.js"]
}
],
...
}
Nome | Tipo | Descrição |
---|---|---|
matches |
matriz de strings | Obrigatório. Especifica em quais páginas esse script de conteúdo será injetado. Consulte Padrões de correspondência para detalhes sobre a sintaxe dessas strings e Padrões de correspondência e globs para informações sobre como excluir URLs. |
css |
matriz de strings | Opcional. A lista de arquivos CSS a serem injetados nas páginas correspondentes. São injetados na ordem em que aparecem nesta matriz, antes de qualquer DOM ser construído ou exibido da página. |
js |
|
Opcional. A lista de arquivos JavaScript a serem injetados em páginas correspondentes. Arquivos são injetados na ordem em que aparecem nesta matriz. Cada string nessa lista deve conter um caminho relativo para um recurso no diretório raiz da extensão. As barras iniciais (`/`) são cortado automaticamente. |
run_at |
RunAt | Opcional. Especifica quando o script deve ser injetado na página. O valor padrão é document_idle . |
match_about_blank |
booleano | Opcional. Define se o script precisa injetar em um frame about:blank .
em que o frame pai ou de abertura corresponde a um dos padrões declarados em
matches . O padrão é "false". |
match_origin_as_fallback |
booleano |
Opcional. Se o script deve injetar em frames que foram
criadas por uma origem correspondente, mas cujo URL ou origem não pode diretamente
correspondem ao padrão. Isso inclui frames com esquemas diferentes, como
about: , data: , blob: e
filesystem: . Consulte também
Injeção de frames relacionados.
|
world |
ExecutionWorld |
Opcional. O mundo JavaScript no qual um script pode ser executado. O padrão é ISOLATED . Consulte também
Trabalhe em mundos isolados.
|
Injetar com declarações dinâmicas
Scripts de conteúdo dinâmico são úteis quando os padrões de correspondência de scripts de conteúdo são não é muito conhecido ou quando os scripts de conteúdo não devem sempre ser injetados em hosts conhecidos.
Apresentadas no Chrome 96, as declarações dinâmicas são semelhantes às funções estáticas
declarações, mas o objeto do script de conteúdo é registrado no Chrome usando
no namespace chrome.scripting
, e não
manifest.json. A API Scripting também permite que os desenvolvedores de extensões
para:
- Registre scripts de conteúdo.
- Receba uma lista de scripts de conteúdo registrados.
- Atualize a lista de scripts de conteúdo registrados.
- Remover scripts de conteúdo registrado
Assim como as declarações estáticas, as declarações dinâmicas podem incluir arquivos JavaScript, CSS ou ambos.
service-worker.js
chrome.scripting
.registerContentScripts([{
id: "session-script",
js: ["content.js"],
persistAcrossSessions: false,
matches: ["*://example.com/*"],
runAt: "document_start",
}])
.then(() => console.log("registration complete"))
.catch((err) => console.warn("unexpected error", err))
service-worker.js
chrome.scripting
.updateContentScripts([{
id: "session-script",
excludeMatches: ["*://admin.example.com/*"],
}])
.then(() => console.log("registration updated"));
service-worker.js
chrome.scripting
.getRegisteredContentScripts()
.then(scripts => console.log("registered content scripts", scripts));
service-worker.js
chrome.scripting
.unregisterContentScripts({ ids: ["session-script"] })
.then(() => console.log("un-registration complete"));
Injetar programaticamente
Use a injeção programática para scripts de conteúdo que precisam ser executados em resposta a eventos ou em ocasiões.
Para injetar um script de conteúdo de forma programática, sua extensão precisa de permissões de host para
a página na qual está tentando injetar scripts. As permissões de host podem ser concedidas por
solicitando-as como parte do manifesto da sua extensão ou temporariamente usando "activeTab"
.
Confira a seguir versões diferentes de uma extensão ActiveTab.
manifest.json:
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
Os scripts de conteúdo podem ser injetados como arquivos.
content-script.js
document.body.style.backgroundColor = "orange";
service-worker.js:
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ["content-script.js"]
});
});
Como alternativa, o corpo de uma função pode ser injetado e executado como um script de conteúdo.
service-worker.js:
function injectedFunction() {
document.body.style.backgroundColor = "orange";
}
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
});
});
A função injetada é uma cópia da função referenciada no
chrome.scripting.executeScript()
, e não a função original em si. Como resultado, o valor
o corpo deve ser autônomo; referências a variáveis fora da função fará com que o conteúdo
para gerar uma ReferenceError
.
Ao injetar como uma função, você também pode transmitir argumentos para a função.
service-worker.js
function injectedFunction(color) {
document.body.style.backgroundColor = color;
}
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
args : [ "orange" ],
});
});
Excluir correspondências e globs
Para personalizar a correspondência de página especificada, inclua os seguintes campos em uma declaração registro.
Nome | Tipo | Descrição |
---|---|---|
exclude_matches |
matriz de strings | Opcional. Exclui páginas em que o script de conteúdo seria injetado de outra forma entrar. Consulte Padrões de correspondência para detalhes sobre a sintaxe de essas strings. |
include_globs |
matriz de strings | Opcional. Aplicado após matches para incluir apenas os URLs que também
correspondem a esse glob. O objetivo é emular o @include
Palavra-chave "Greasemonkey". |
exclude_globs |
matriz de strings | Opcional. Aplicado após matches para excluir URLs correspondentes
massa. que serve para emular o @exclude .
Palavra-chave "Greasemonkey". |
O script de conteúdo será injetado em uma página se ambas as condições forem verdadeiras:
- O URL corresponde a qualquer padrão
matches
e qualquer padrãoinclude_globs
. - O URL também não corresponde a um padrão
exclude_matches
ouexclude_globs
. Como a propriedadematches
é obrigatória,exclude_matches
,include_globs
eexclude_globs
só pode ser usado para limitar as páginas que serão afetadas.
A extensão a seguir injeta o script de conteúdo em https://www.nytimes.com/health
mas não em https://www.nytimes.com/business
.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_matches": ["*://*/*business*"],
"js": ["contentScript.js"]
}
],
...
}
service-worker.js
chrome.scripting.registerContentScripts([{
id : "test",
matches : [ "https://*.nytimes.com/*" ],
excludeMatches : [ "*://*/*business*" ],
js : [ "contentScript.js" ],
}]);
As propriedades glob seguem uma sintaxe diferente e mais flexível dos padrões de correspondência. Globo aceitável
strings são URLs que podem conter "caractere curinga" asteriscos 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
qualquer caractere único.
Por exemplo, o glob https://???.example.com/foo/\*
corresponde a qualquer um dos seguintes itens:
https://www.example.com/foo/bar
https://the.example.com/foo/
No entanto, ele não corresponde ao seguinte:
https://my.example.com/foo/bar
https://example.com/foo/
https://www.example.com/foo
Essa extensão injeta o script de conteúdo em https://www.nytimes.com/arts/index.html
e
https://www.nytimes.com/jobs/index.htm*
, mas não para
https://www.nytimes.com/sports/index.html
:
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"include_globs": ["*nytimes.com/???s/*"],
"js": ["contentScript.js"]
}
],
...
}
Essa extensão injeta o script de conteúdo em https://history.nytimes.com
e
https://.nytimes.com/history
, mas não em https://science.nytimes.com
ou
https://www.nytimes.com/science
:
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
Uma, todas ou algumas delas podem ser incluídas para atingir o escopo correto.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_matches": ["*://*/*business*"],
"include_globs": ["*nytimes.com/???s/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
Tempo de execução
O campo run_at
controla quando arquivos JavaScript são injetados na página da Web. As APIs
o valor padrão é "document_idle"
. Veja o tipo RunAt para outras possíveis
valores.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"run_at": "document_idle",
"js": ["contentScript.js"]
}
],
...
}
service-worker.js
chrome.scripting.registerContentScripts([{
id : "test",
matches : [ "https://*.nytimes.com/*" ],
runAt : "document_idle",
js : [ "contentScript.js" ],
}]);
Nome | Tipo | Descrição |
---|---|---|
document_idle |
string | Preferencial. Use "document_idle" sempre que possível.O navegador escolhe um momento para injetar scripts entre "document_end" e imediatamente após
a window.onload
é acionado. O momento exato da injeção depende da complexidade do documento e da
tempo que leva para carregar e é otimizada para a velocidade de carregamento da página.Scripts de conteúdo em execução em "document_idle" não precisam ouvir o evento
window.onload , eles vão ser executados após a conclusão do DOM. Se um
o script precisa ser executado depois de window.onload , a extensão poderá verificar se
onload já disparou usando document.readyState
. |
document_start |
string | Os scripts são injetados após qualquer arquivo de css , mas antes de qualquer outro DOM
construído ou qualquer outro script é executado. |
document_end |
string | Os scripts são injetados imediatamente após a conclusão do DOM, mas antes dos sub-recursos, como imagens e frames foram carregados. |
Especificar frames
O campo "all_frames"
permite que a extensão especifique se os arquivos JavaScript e CSS devem ser
injetada em todos os frames que correspondem aos requisitos de URL especificados ou somente no frame superior de um
.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"all_frames": true,
"js": ["contentScript.js"]
}
],
...
}
service-worker.js
chrome.scripting.registerContentScripts([{
id: "test",
matches : [ "https://*.nytimes.com/*" ],
allFrames : true,
js : [ "contentScript.js" ],
}]);
Nome | Tipo | Descrição |
---|---|---|
all_frames |
booleano | Opcional. O padrão é false , o que significa que apenas o frame superior é
correspondentes.Se true for especificado, todos os frames serão injetados, mesmo se o
não é o frame superior da guia. O URL de cada frame é verificado independentemente
e cumprimento de requisitos regulatórios. Ele não será injetado em frames filhos se os requisitos de URL não forem atendidos. |
Injetar frames relacionados
As extensões podem executar scripts em frames relacionados a uma mas não correspondem por si só. Um cenário comum quando esse é o caso é para frames com URLs que foram criados por um frame correspondente, mas cujos URLs não correspondem aos padrões especificados do script.
Isso acontece quando uma extensão quer injetar em frames com URLs que
tem esquemas about:
, data:
, blob:
e filesystem:
. Nesses casos,
O URL não corresponderá ao padrão do script de conteúdo (e, no caso de about:
e
data:
, nem mesmo o URL pai ou a origem são incluídos no URL
nenhuma, como about:blank
ou data:text/html,<html>Hello, World!</html>
).
No entanto, esses frames ainda podem ser associados ao frame criado.
Para injetar nesses frames, as extensões podem especificar o
"match_origin_as_fallback"
em uma especificação de script de conteúdo na
manifesto do aplicativo.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.google.com/*"],
"match_origin_as_fallback": true,
"js": ["contentScript.js"]
}
],
...
}
Quando especificado e definido como true
, o Chrome analisa a origem do
iniciador do frame para determinar se ele corresponde, e não
o URL do próprio frame. Ela também pode ser diferente da
a origin do frame de destino (por exemplo, data:
URL tem uma origem nula).
O iniciador do frame é o frame que criou ou navegou até o destino frame. Embora ele seja comumente o pai direto ou quem abre, pode não ser (como em o caso de um frame navegar em um iframe dentro de um iframe).
Como isso compara a origin do frame iniciador, o frame iniciador
poderia estar em qualquer caminho dessa origem. Para esclarecer essa implicação, o Chrome
requer scripts de conteúdo especificados com "match_origin_as_fallback"
Defina como true
para também especificar um caminho de *
.
Quando "match_origin_as_fallback"
e "match_about_blank"
são especificados,
"match_origin_as_fallback"
tem prioridade.
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, elas 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, ele deverá fazer isso pelo DOM compartilhado.
Um exemplo pode ser alcançado usando window.postMessage()
:
content-script.js
var port = chrome.runtime.connect();
window.addEventListener("message", (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);
example.js
document.getElementById("theButton").addEventListener("click", () => {
window.postMessage(
{type : "FROM_PAGE", text : "Hello from the webpage!"}, "*");
}, false);
A página sem extensão, example.html, publica mensagens nela mesma. Esta mensagem foi interceptada e inspecionado pelo script de conteúdo e postado no processo de extensão. Dessa forma, a página estabelece uma linha de comunicação com o processo de extensão. O contrário é possível de meios semelhantes.
Acessar arquivos de extensão
Para acessar um arquivo de extensão a partir de um script de conteúdo, você pode chamar
chrome.runtime.getURL()
para encontrar o URL absoluto do seu recurso de extensão, conforme mostrado no exemplo a seguir (content.js
):
content-script.js
let image = chrome.runtime.getURL("images/my_image.png")
Para usar fontes ou imagens em um arquivo CSS, use @@extension_id
para construir um URL, conforme mostrado no exemplo a seguir (content.css
):
content.css
body {
background-image:url('chrome-extension://__MSG_@@extension_id__/background.png');
}
@font-face {
font-family: 'Stint Ultra Expanded';
font-style: normal;
font-weight: 400;
src: url('chrome-extension://__MSG_@@extension_id__/fonts/Stint Ultra Expanded.woff') format('woff');
}
Todos os recursos precisam ser declarados como recursos acessíveis pela Web no arquivo manifest.json
:
manifest.json
{
...
"web_accessible_resources": [
{
"resources": [ "images/*.png" ],
"matches": [ "https://example.com/*" ]
},
{
"resources": [ "fonts/*.woff" ],
"matches": [ "https://example.com/*" ]
}
],
...
}
Proteja-se
Embora os 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 chamar fetch()
, tenha cuidado para filtrar o conteúdo de acordo
scripting em vários sites antes de injetá-lo. Comunique-se apenas por HTTPS
evitar ataques "man-in-the-middle".
Filtre páginas da Web maliciosas. Por exemplo, os padrões a seguir são perigosos e não permitidas no Manifesto V3:
content-script.js
const data = document.getElementById("json-data"); // WARNING! Might be evaluating an evil script! const parsed = eval("(" + data + ")");
content-script.js
const 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:
content-script.js
const data = document.getElementById("json-data") // JSON.parse does not evaluate the attacker's scripts. const parsed = JSON.parse(data);
content-script.js
const elmt_id = ... // The closure form of setTimeout does not evaluate scripts. window.setTimeout(() => animate(elmt_id), 200);