Secuencias de comandos de contenido

Las secuencias de comandos de contenido son archivos que se ejecutan en el contexto de las páginas web. Mediante el Modelo de objetos del documento (DOM) estándar, pueden leer los 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 capacidades de las secuencias de comandos del contenido

Las secuencias de comandos de contenido pueden acceder a las APIs de Chrome que usa la extensión superior intercambiando mensajes con la extensión. 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 API de Chrome:

Las secuencias de comandos de contenido no pueden acceder a otras API de forma directa.

Trabaja en mundos aislados

Las secuencias de comandos de contenido se encuentran en un mundo aislado, lo que permite que una secuencia de comandos de contenido realice cambios en su entorno de JavaScript sin entrar en conflicto con la página ni con secuencias de comandos de contenido adicionales.

Se puede ejecutar una extensión 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);

Ambas alertas aparecerán si se presionara el botón.

Los mundos aislados no permiten que las secuencias de comandos de contenido, la extensión ni la página web accedan a las variables o funciones creadas por las demás. Esto también proporciona a las secuencias de comandos de contenido la capacidad de habilitar una funcionalidad a la que la página web no debería poder acceder.

Incorporar 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 secuencias de comandos de contenido que deban ejecutarse en ocasiones específicas.

Para insertar una secuencia de comandos de contenido programático, 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 del contenido se ejecute en la pestaña activa actual sin especificar los 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 insertar un archivo completo.

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

Cómo inyectar de forma declarativa

Usa la inserción declarativa para las secuencias de comandos de contenido que deben ejecutarse automáticamente en páginas específicas.

Las secuencias de comandos insertadas de forma declarativa se registran en el manifiesto, en el campo "content_scripts". Pueden incluir archivos JavaScript, 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 {: #coincidencias} arreglo 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 strings y Patrones y globs de coincidencia para obtener información sobre cómo excluir URLs.
css {: #css } arreglo de cadenas Opcional. La lista de archivos CSS que se insertarán en las páginas coincidentes. Estos se insertan en el orden en que aparecen en esta matriz, antes de crear o mostrar cualquier DOM para la página.
js {: #js } arreglo de cadenas Opcional. La lista de archivos JavaScript que se insertarán en páginas coincidentes. Estos se insertan en el orden en que aparecen en este array.
match_about_blank {: #match_about_blank } boolean Opcional. Indica si la secuencia de comandos debe insertarse en un marco about:blank en el que el marco superior o de apertura coincide con uno de los patrones declarados en matches. La configuración predeterminada es false.

Excluir coincidencias y globs

La coincidencia de página especificada se puede personalizar si incluye los siguientes campos en el registro del manifiesto.

Nombre Tipo Descripción
exclude_matches {: #excluir_coincidencias} arreglo de cadenas Opcional. Se excluyen las páginas en las que se insertaría esta secuencia de comandos de contenido. Consulta Patrones de coincidencia para obtener más detalles sobre la sintaxis de estas strings.
include_globs {: #include_globs } arreglo de cadenas Opcional. Se aplica después de matches para incluir solo las URLs que también coinciden con este glob. Tiene como objetivo emular la palabra clave Greasemonkey @include.
exclude_globs {: #excluir_globs } arreglo de string Opcional. Se aplica después de matches para excluir las URLs que coinciden con este glob. Tiene como objetivo emular la palabra clave @excludeGreasemonkey.

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

Dado que la propiedad matches es obligatoria, exclude_matches, include_globs y exclude_globs solo se pueden usar para limitar las páginas que 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 tienen una sintaxis diferente y más flexible que los patrones de coincidencia. Las strings glob aceptables son las URL que pueden contener asteriscos comodines 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 con cualquier carácter.

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

  • 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 del 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 pueden incluir uno, todos o algunos de ellos 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

Cuando los archivos JavaScript se insertan en la página web se controla mediante el campo run_at. 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 } cadena Preferido. Usa "document_idle" siempre que sea posible.

El navegador elige un momento para insertar secuencias de comandos entre el "document_end" e inmediatamente después de que se activa el evento windowonload. El momento exacto de la inserción depende de qué tan complejo sea el documento y cuánto tarde en cargarse; además, 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; se garantiza que se ejecutarán una vez que se complete el DOM. Si una secuencia de comandos debe ejecutarse después de window.onload, la extensión puede verificar si ya se activó onload mediante la propiedad document.readyState.
document_start {: #document_start } cadena Las secuencias de comandos se insertan después de los archivos de css, pero antes de que se construya cualquier otro DOM o se ejecute cualquier otra secuencia de comandos.
document_end {: #document_end } cadena 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.

Cómo especificar marcos

El campo "all_frames" permite que la extensión especifique si los archivos JavaScript y CSS deben insertarse en todos los marcos que coinciden con los requisitos de URL especificados 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 } boolean Opcional. El valor predeterminado es false, lo que significa que solo coincide el fotograma superior.

Si se especifica true, se insertará en todos los fotogramas, incluso si no es el fotograma superior de la pestaña. Cada marco se comprueba de forma independiente para verificar los requisitos de URL; no se insertará en los marcos secundarios si no se cumplen los requisitos de 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 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 la secuencia de comandos de contenido, debe hacerlo mediante el DOM compartido.

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

La página sin extensión, example.html, publica mensajes en sí misma. La secuencia de comandos de contenido intercepta e inspecciona 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 contrario 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 la página web. Si la secuencia de comandos de contenido recibe contenido de un sitio web independiente, como cuando se hace una XMLHttpRequest, ten cuidado de filtrar los ataques de secuencias de comandos entre sitios de contenido antes de insertarlo. Comunícate solo a través de HTTPS para evitar ataques de "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, es preferible usar 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);