Las secuencias de comandos de contenido son archivos que se ejecutan en el contexto de páginas web. Con el estándar Document Object Model (DOM), pueden leer detalles de las páginas web que visita el navegador, realizar los cambios y pasar información a su extensión superior.
Comprende las capacidades de las secuencias de comandos de contenido
Las secuencias de comandos de contenido pueden acceder directamente a las siguientes APIs de extensión:
dom
i18n
storage
runtime.connect()
runtime.getManifest()
runtime.getURL()
runtime.id
runtime.onConnect
runtime.onMessage
runtime.sendMessage()
Las secuencias de comandos de contenido no pueden acceder directamente a otras APIs. Sin embargo, pueden acceder a ellos de forma indirecta intercambiando mensajes con otras partes de tu extensión.
También puedes acceder a otros archivos de tu extensión desde una secuencia de comandos de contenido con
APIs como fetch()
. Para hacerlo, deberás declararlos como
recursos accesibles a través de la Web. Ten en cuenta que esto también expone los recursos a cualquier
secuencias de comandos propias o de terceros que se ejecuten en el mismo sitio.
Trabajar en mundos aislados
Los guiones de contenido viven en un mundo aislado, lo que permite que un script de contenido realice cambios en su Entorno de JavaScript sin entrar en conflicto con la página ni otras extensiones los guiones de contenido.
Una extensión puede ejecutarse en una página web con un código similar al siguiente ejemplo.
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>
Esa extensión podría insertar la siguiente secuencia de comandos de contenido usando una de las técnicas descritas en el Sección Inyecta secuencias de comandos.
content-script.js
var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener(
"click", () => alert(greeting + button.person_name + "."), false);
Con este cambio, ambas alertas aparecerán en secuencia cuando se haga clic en el botón.
Cómo insertar secuencias de comandos
Las secuencias de comandos de contenido se pueden declarar de forma estática, declarar de forma dinámica o inyectada de manera programática.
Cómo inyectar con declaraciones estáticas
Usa declaraciones de secuencias de comandos de contenido estático en manifest.json para las secuencias de comandos que se deben enviar automáticamente. se ejecutan en un conjunto de páginas conocido.
Las secuencias de comandos declaradas de forma estática se registran en el manifiesto con la clave "content_scripts"
.
Pueden incluir archivos JavaScript, CSS o ambos. Todas las secuencias de comandos
de contenido de ejecución automática deben especificar
coincide con los patrones.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"css": ["my-styles.css"],
"js": ["content-script.js"]
}
],
...
}
Nombre | Tipo | Descripción |
---|---|---|
matches |
array de cadenas | Obligatorio. Especifica en qué páginas se insertará esta secuencia de comandos de contenido. Consulta Patrones de coincidencia para obtener detalles sobre la sintaxis de estas cadenas y Ajustar patrones y globs para obtener información sobre cómo excluir URLs. |
css |
array de cadenas | Opcional. La lista de archivos CSS que se insertarán en páginas coincidentes. Son Se insertan en el orden en que aparecen en este array, antes de que se construya o se muestre cualquier DOM. para la página. |
js |
|
Opcional. La lista de archivos JavaScript que se insertarán en las páginas coincidentes. Archivos se insertan en el orden en que aparecen en este array. Cada cadena de esta lista debe contener una ruta de acceso relativa a un recurso en el directorio raíz de la extensión. Las barras iniciales (`/`) se se cortó automáticamente. |
run_at |
RunAt | Opcional. Especifica cuándo se debe insertar la secuencia de comandos en la página. La configuración predeterminada es
document_idle |
match_about_blank |
boolean | Opcional. Si la secuencia de comandos debe insertarse en un marco about:blank
donde el marco superior o de apertura coincide con uno de los patrones declarados en
matches La configuración predeterminada es "false". |
match_origin_as_fallback |
boolean |
Opcional. Si la secuencia de comandos debe insertar o no marcos que no fueron
creado por un origen coincidente, pero cuya URL u origen pueden no estar directamente
coinciden con el patrón. Estas incluyen marcos con diferentes esquemas, como
about: , data: , blob: y
filesystem: Consulta también
Inyección en marcos relacionados:
|
world |
ExecutionWorld |
Opcional. El mundo de JavaScript en el que se ejecutará una secuencia de comandos. La configuración predeterminada es ISOLATED . Consulta también
Trabaja en mundos aislados.
|
Cómo insertar con declaraciones dinámicas
Las secuencias de comandos de contenido dinámico son útiles cuando los patrones de coincidencia de las secuencias de comandos de contenido son o cuando las secuencias de comandos de contenido no siempre deberían insertarse en hosts conocidos.
Las declaraciones dinámicas se introdujeron en Chrome 96 y son similares a las estáticas.
de secuencia de comandos, pero el objeto de secuencia de comandos de contenido se registra en Chrome usando
en el espacio de nombres chrome.scripting
y no en
manifest.json. La API de Scripting también permite a los desarrolladores de extensiones
a:
- Registrar las secuencias de comandos del contenido
- Obtén una lista de las secuencias de comandos de contenido registradas.
- Actualiza la lista de secuencias de comandos de contenido registradas.
- Quita las secuencias de comandos de contenido registradas.
Al igual que las declaraciones estáticas, las declaraciones dinámicas pueden incluir archivos JavaScript, CSS o 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"));
Cómo inyectar de manera programática
Usa la inyección programática para secuencias de comandos de contenido que deban ejecutarse en respuesta a eventos o en en algunas ocasiones.
Para insertar una secuencia de comandos de contenido de manera programática, tu extensión necesita permisos de host para lo siguiente:
la página en la que intenta insertar secuencias de comandos. Los permisos de host se otorgan
solicitándolas como parte del manifiesto de tu extensión o temporalmente usando "activeTab"
.
Las siguientes son versiones diferentes de una extensión basada en activeTab.
manifest.json:
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
Las secuencias de comandos de contenido se pueden insertar como archivos.
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"]
});
});
O bien, el cuerpo de una función se puede insertar y ejecutar como una secuencia de comandos de contenido.
service-worker.js:
function injectedFunction() {
document.body.style.backgroundColor = "orange";
}
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
});
});
Ten en cuenta que la función inyectada es una copia de la función a la que se hace referencia en la
chrome.scripting.executeScript()
, no la función original en sí. Como resultado, el espacio de nombres
El cuerpo debe ser independiente. las referencias a variables fuera de la función causarán que el contenido
secuencia de comandos para arrojar una ReferenceError
Cuando inyectas como una función, también puedes pasar argumentos a la función.
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 coincidencias y globs
Para personalizar las coincidencias de páginas especificadas, incluye los siguientes campos en un archivo de registro.
Nombre | Tipo | Descripción |
---|---|---|
exclude_matches |
array de cadenas | Opcional. Excluye las páginas en las que se insertaría esta secuencia de comandos de contenido de otro modo a los que puedes acceder. Consulta Patrones de coincidencia para obtener detalles sobre la sintaxis de estas cadenas de texto. |
include_globs |
array de cadenas | Opcional. Se aplica después del matches para incluir solo las URLs que también
coincidir con este glob. Esto tiene como objetivo emular la @include .
Palabra clave Greasemonkey. |
exclude_globs |
array de cadenas | Opcional. Se aplica después del matches para excluir las URLs que coincidan con esta
glob. Su objetivo es emular la @exclude .
Palabra clave Greasemonkey. |
La secuencia de comandos de contenido se insertará en una página si se cumplen estas dos condiciones:
- Su URL coincide con cualquier patrón
matches
yinclude_globs
. - La URL tampoco coincide con un patrón
exclude_matches
oexclude_globs
. Debido a que la propiedadmatches
es obligatoria,exclude_matches
,include_globs
yexclude_globs
solo se puede usar para limitar las páginas que se verán afectadas.
La siguiente extensión inserta la secuencia de comandos de contenido en https://www.nytimes.com/health
pero no en 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" ],
}]);
Las propiedades glob tienen una sintaxis diferente y más flexible que los patrones de coincidencia. glob aceptable
las cadenas son URLs que pueden contener "comodín" asteriscos y signos de interrogación. El asterisco (*
)
coincide con cualquier cadena de cualquier longitud, incluida la cadena vacía, mientras que el signo de interrogación (?
) coincide
cualquier carácter único.
Por ejemplo, el glob https://???.example.com/foo/\*
coincide con cualquiera de las siguientes opciones:
https://www.example.com/foo/bar
https://the.example.com/foo/
Sin embargo, no coincide con lo siguiente:
https://my.example.com/foo/bar
https://example.com/foo/
https://www.example.com/foo
Esta extensión inserta la secuencia de comandos de contenido en https://www.nytimes.com/arts/index.html
y
https://www.nytimes.com/jobs/index.htm*
, pero no en
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"]
}
],
...
}
Esta extensión inserta la secuencia de comandos de contenido en https://history.nytimes.com
y
https://.nytimes.com/history
, pero no en https://science.nytimes.com
o
https://www.nytimes.com/science
:
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
Se puede incluir una, todas o algunas de ellas para lograr el alcance correcto.
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"]
}
],
...
}
Tiempo de ejecución
El campo run_at
controla cuándo se insertan los archivos JavaScript en la página web. Las opciones preferidas y
el valor predeterminado es "document_idle"
. Consulta el tipo RunAt para ver otras opciones posibles.
de salida.
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" ],
}]);
Nombre | Tipo | Descripción |
---|---|---|
document_idle |
string | Preferido. Usa "document_idle" siempre que sea posible.El navegador Elige un momento para insertar secuencias de comandos entre "document_end" e inmediatamente después
window.onload
se activa el evento. El momento exacto de la inserción depende de qué tan complejo sea el documento y qué tan
tarda en cargarse y está optimizado para acelerar la velocidad de carga de la página.Secuencias de comandos de contenido que se ejecuta a las "document_idle" , no necesitan escuchar el
window.onload , se garantiza que se ejecutarán una vez que se complete el DOM. Si un
de comandos debe ejecutarse después de window.onload , la extensión puede
onload ya se activó con document.readyState
propiedad. |
document_start |
string | Las secuencias de comandos se insertan después de cualquier archivo de css , pero antes de que se realice cualquier otro DOM
se construye o se ejecuta cualquier otra secuencia de comandos. |
document_end |
string | Las secuencias de comandos se insertan inmediatamente después de que se completa el DOM, pero antes de que los subrecursos como se cargaron las imágenes y los marcos. |
Cómo especificar marcos
El campo "all_frames"
permite que la extensión especifique si los archivos JavaScript y CSS deben
insertar en todos los marcos que coincidan con los requisitos de URL especificados o solo en el marco superior de una
.
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" ],
}]);
Nombre | Tipo | Descripción |
---|---|---|
all_frames |
boolean | Opcional. El valor predeterminado es false , lo que significa que solo se muestra el marco superior.
Si se especifica true , se insertarán todos los fotogramas, incluso si
no es el marco superior de la pestaña. Cada marco se verifica por separado para la URL
y los requisitos de cumplimiento. No se insertará en marcos secundarios si no se cumplen los requisitos de URL. |
Cómo inyectar en marcos relacionados
Es posible que las extensiones quieran ejecutar secuencias de comandos en marcos relacionados con una coincidencia un marco, pero no coinciden. Una situación común cuando este es el caso es para marcos con URL creadas por un marco coincidente, pero cuyas URL no coinciden con los patrones especificados de la secuencia de comandos.
Esto ocurre cuando una extensión quiere inyectar en marcos con URLs que
tienen los esquemas about:
, data:
, blob:
y filesystem:
. En estos casos, el
URL no coincidirá con el patrón de la secuencia de comandos del contenido (y, en el caso de about:
y
data:
, ni siquiera incluyas la URL principal ni el origen en la URL
en absoluto, como en about:blank
o data:text/html,<html>Hello, World!</html>
).
Sin embargo, estos marcos aún pueden asociarse con el marco de creación.
Para insertar en estos marcos, las extensiones pueden especificar
La propiedad "match_origin_as_fallback"
en una especificación de secuencia de comandos de contenido en el
.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.google.com/*"],
"match_origin_as_fallback": true,
"js": ["contentScript.js"]
}
],
...
}
Cuando se especifica y se establece en true
, Chrome buscará el origen del
iniciador del fotograma para determinar si este coincide, en lugar de
la URL del marco en sí. Ten en cuenta que esto también podría ser diferente del
origen del marco de destino (p.ej., data:
de URLs tienen un origen nulo).
El iniciador del fotograma es el que creó el objetivo o navegó por él marco. Si bien suele ser el elemento principal directo o la introducción, es posible que no lo sea (como el caso de un marco que navega por un iframe dentro de un iframe).
Como se compara el origen del marco iniciador, este último
podría estar en cualquier ruta desde ese origen. Para dejar en claro esta implicación, Chrome
requiere cualquier secuencia de comandos de contenido especificada con "match_origin_as_fallback"
Se configura en true
para especificar también una ruta de acceso de *
.
Cuando se especifican "match_origin_as_fallback"
y "match_about_blank"
,
"match_origin_as_fallback"
tiene prioridad.
Comunicación con la página de incorporación
Aunque los entornos de ejecución de las secuencias de comandos de contenido y las páginas que los alojan están aislados, entre sí, comparten el acceso al DOM de la página. Si la página desea comunicarse con el secuencia de comandos del contenido, o con la extensión a través de la secuencia de comandos del contenido, debe hacerlo a través del DOM compartido.
Se puede lograr un ejemplo con 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);
La página sin extensión, example.html, publica mensajes en sí misma. Este mensaje se intercepta inspeccionado por la secuencia de comandos del contenido y, luego, publicado en el proceso de extensión. De esta manera, la página establece una línea de comunicación con el proceso de extensión. Es posible lo contrario. medios similares.
Cómo acceder a los archivos de extensión
Para acceder a un archivo de extensión desde una secuencia de comandos de contenido, puedes llamar a
chrome.runtime.getURL()
para obtener la URL absoluta de tu recurso de extensión, como se muestra en el siguiente ejemplo (content.js
):
content-script.js
let image = chrome.runtime.getURL("images/my_image.png")
Para usar fuentes o imágenes en un archivo CSS, puedes usar @@extension_id
para crear una URL como se muestra en el siguiente ejemplo (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 los activos se deben declarar como recursos accesibles desde la Web en el archivo manifest.json
:
manifest.json
{
...
"web_accessible_resources": [
{
"resources": [ "images/*.png" ],
"matches": [ "https://example.com/*" ]
},
{
"resources": [ "fonts/*.woff" ],
"matches": [ "https://example.com/*" ]
}
],
...
}
Mantente protegido
Si bien los mundos aislados brindan una capa de protección, el uso de secuencias de comandos de contenido
vulnerabilidades en una extensión y en la página web. Si la secuencia de comandos de contenido recibe contenido de un
sitio web independiente, como llamar a fetch()
, ten cuidado de filtrar el contenido según
ataques de secuencia de comandos entre sitios antes de insertarlo. Solo comunícate a través de HTTPS para
Evita los ataques de "man-in-the-middle".
Asegúrate de filtrar las páginas web maliciosas. Por ejemplo, los siguientes patrones son peligrosos y lo que no está permitido en Manifest 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);
En su lugar, usa APIs más seguras que no ejecuten secuencias de comandos:
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);