Secuencias de comandos de contenido

Las secuencias de comandos de contenido son archivos que se ejecutan en el contexto de las páginas web. Con el uso del modelo de objetos del documento (DOM) estándar, pueden leer detalles de las páginas web que visita el navegador, realizar cambios en ellas y pasar información a su extensión superior.

Comprende las funciones de las secuencias de comandos de contenido

Las secuencias de comandos de contenido pueden intercambiar mensajes con la extensión superior para acceder a las APIs de Chrome que usa. También pueden acceder a la URL del archivo de una extensión con chrome.runtime.getURL() y usar el resultado de la misma manera que otras URLs.

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

Además, la secuencia de comandos de contenido puede acceder directamente a las siguientes APIs de Chrome:

Las secuencias de comandos de contenido no pueden acceder a otras APIs directamente.

Trabajar en mundos aislados

Las secuencias de comandos del contenido viven en un mundo aislado, lo que les permite realizar cambios en su entorno de JavaScript sin entrar en conflicto con la página o con secuencias de comandos del contenido adicionales.

Una extensión puede ejecutarse en una página web con un código similar al siguiente ejemplo.

<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>

Esa extensión podría insertar la siguiente secuencia de comandos de contenido.

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

Si se presionara el botón, aparecerían ambas alertas.

Los mundos aislados no permiten que las secuencias de comandos de contenido, la extensión ni la página web accedan a ninguna variable ni función creada por los demás. Esto también permite que las secuencias de comandos de contenido habiliten funciones a las que no debería tener acceso la página web.

Cómo insertar secuencias de comandos

Las secuencias de comandos de contenido se pueden insertar de manera programática o declarativa.

Cómo insertar de manera programática

Usa la inserción programática para las secuencias de comandos de contenido que deben ejecutarse en ocasiones específicas.

Para insertar una secuencia de comandos de contenido programática, proporciona el permiso activeTab en el manifiesto. Esto otorga acceso seguro al host del sitio activo y acceso temporal al permiso de pestañas, lo que permite que la secuencia de comandos de contenido se ejecute en la pestaña activa actual sin especificar permisos de origen cruzado.

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

Las secuencias de comandos de contenido se pueden insertar como código.

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

También se puede inyectar un archivo completo.

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

Cómo insertar de forma declarativa

Usa la inserción declarativa para las secuencias de comandos de contenido que se deben ejecutar automáticamente en páginas especificadas.

Las secuencias de comandos insertadas de forma declarativa se registran en el manifiesto en el campo "content_scripts". Pueden incluir archivos JavaScript, archivos CSS o ambos. Todas las secuencias de comandos de contenido de ejecución automática deben especificar patrones de coincidencia.

{
 "name": "My extension",
 ...
 "content_scripts": [
   {
     "matches": ["http://*.nytimes.com/*"],
     "css": ["myStyles.css"],
     "js": ["contentScript.js"]
   }
 ],
 ...
}
Nombre Tipo Descripción
matches {: #matches } array de cadenas Obligatorio. Especifica en qué páginas se insertará esta secuencia de comandos de contenido. Consulta Patrones de coincidencia para obtener más detalles sobre la sintaxis de estas cadenas y Patrones de coincidencia y globs para obtener información sobre cómo excluir URLs.
css {: #css } array de cadenas Opcional. Es la lista de archivos CSS que se insertarán en las páginas coincidentes. Se insertan en el orden en que aparecen en este array, antes de que se construya o muestre ningún DOM para la página.
js {: #js } array de cadenas Opcional. Es la lista de archivos JavaScript que se insertarán en las páginas coincidentes. Se insertan en el orden en que aparecen en este array.
match_about_blank {: #match_about_blank } booleano Opcional. Indica si la secuencia de comandos debe insertarse en un marco about:blank en el que el marco superior o de apertura coincida con uno de los patrones declarados en matches. La configuración predeterminada es false.

Excluye coincidencias y globs

Para personalizar la coincidencia de páginas especificada, incluye los siguientes campos en el registro del manifiesto.

Nombre Tipo Descripción
exclude_matches {: #exclude_matches } array de cadenas Opcional. Excluye las páginas en las que, de otro modo, se insertaría esta secuencia de comandos de contenido. Consulta Patrones de coincidencia para obtener más detalles sobre la sintaxis de estas cadenas.
include_globs {: #include_globs } array de cadenas Opcional. Se aplica después de matches para incluir solo aquellas URLs que también coincidan con este glob. Su objetivo es emular la palabra clave @include de Greasemonkey.
exclude_globs {: #exclude_globs } array de cadenas Opcional. Se aplica después de matches para excluir las URLs que coincidan con este glob. Su objetivo es emular la palabra clave @exclude de Greasemonkey.

La secuencia de comandos de contenido se insertará en una página si su URL coincide con cualquier patrón matches y cualquier patrón include_globs, siempre y cuando la URL no coincida con un patrón exclude_matches o exclude_globs.

Debido a que la propiedad matches es obligatoria, exclude_matches, include_globs y exclude_globs solo se pueden usar para limitar qué páginas se verán afectadas.

La siguiente extensión insertaría la secuencia de comandos de contenido en http://www.nytimes.com/ health, pero no en http://www.nytimes.com/ business .

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

Las propiedades glob siguen una sintaxis diferente y más flexible que los patrones de concordancia. Las cadenas de glob aceptables son URLs que pueden contener asteriscos y signos de interrogación comodín. El asterisco * coincide con cualquier cadena de cualquier longitud, incluida la cadena vacía, mientras que el signo de interrogación ? coincide con cualquier carácter.

Por ejemplo, el glob http:// ??? .example.com/foo/ * coincide con cualquiera de los siguientes:

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

Sin embargo, no coincide con lo siguiente:

  • http:// mi .example.com/foo/bar
  • http:// ejemplo .com/foo/
  • http://www.example.com/foo

Esta extensión insertaría la secuencia de comandos de contenido en http:/www.nytimes.com/ arts /index.html y http://www.nytimes.com/ jobs /index.html, pero no en http://www.nytimes.com/ sports /index.html.

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

Esta extensión insertaría la secuencia de comandos de contenido en http:// history .nytimes.com y http://.nytimes.com/ history, pero no en http:// science .nytimes.com ni http://www.nytimes.com/ science .

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

Se puede incluir uno, todos o algunos de estos para lograr el alcance correcto.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.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. El campo preferido y predeterminado es "document_idle", pero también se puede especificar como "document_start" o "document_end" si es necesario.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "run_at": "document_idle",
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Nombre Tipo Descripción
document_idle {: #document_idle } string Preferida. Usa "document_idle" siempre que sea posible.

El navegador elige un momento para insertar secuencias de comandos entre "document_end" y, de inmediato, después de que se activa el evento windowonload. El momento exacto de la inserción depende de la complejidad del documento y del tiempo que tarda en cargarse, y está optimizado para la velocidad de carga de la página.

Las secuencias de comandos de contenido que se ejecutan en "document_idle" no necesitan escuchar el evento window.onload, ya que se garantiza que se ejecutarán después de que se complete el DOM. Si una secuencia de comandos debe ejecutarse después de window.onload, la extensión puede verificar si onload ya se activó con la propiedad document.readyState.
document_start {: #document_start } string Las secuencias de comandos se insertan después de cualquier archivo de css, pero antes de que se construya cualquier otro DOM o se ejecute cualquier otra secuencia de comandos.
document_end {: #document_end } string Las secuencias de comandos se insertan inmediatamente después de que se completa el DOM, pero antes de que se carguen los subrecursos, como las imágenes y los marcos.

Especifica marcos

El campo "all_frames" permite que la extensión especifique si los archivos JavaScript y CSS se deben挿挿inyectar en todos los marcos que coincidan con los requisitos de la URL especificada o solo en el marco superior de una pestaña.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "all_frames": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Nombre Tipo Descripción
all_frames {: #all_frames } booleano Opcional. El valor predeterminado es false, lo que significa que solo se hace coincidir el marco superior.

Si se especifica true, se insertará en todos los marcos, incluso si el marco no es el superior de la pestaña. Cada marco se verifica de forma independiente para los requisitos de la URL y no se insertará en marcos secundarios si no se cumplen los requisitos de la URL.

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 las alojan están aislados entre sí, comparten el acceso al DOM de la página. Si la página desea comunicarse con la secuencia de comandos de contenido o con la extensión a través de ella, debe hacerlo a través del DOM compartido.

Se puede obtener un ejemplo con 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);

La página que no es de extensión, example.html, publica mensajes para sí misma. La secuencia de comandos de contenido intercepta y revisa este mensaje y, luego, lo publica 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. Lo inverso es posible a través de medios similares.

Mantente protegido

Si bien los mundos aislados proporcionan una capa de protección, el uso de secuencias de comandos de contenido puede crear 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 la creación de un XMLHttpRequest, ten cuidado de filtrar los ataques de secuencia de comandos entre sitios antes de insertarlos. Comunícate solo a través de HTTPS para evitar ataques "man-in-the-middle".

Asegúrate de filtrar las páginas web maliciosas. Por ejemplo, los siguientes patrones son peligrosos:

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

En su lugar, prefiere APIs más seguras que no ejecuten secuencias de comandos:

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