Scripts de contenu

Les scripts de contenu sont des fichiers exécutés dans le contexte de pages Web. En utilisant le Document Object Model (DOM) standard, ils peuvent lire les détails des pages Web visitées par le navigateur, les modifier et transmettre des informations à leur extension parente.

Comprendre les fonctionnalités des scripts de contenu

Les scripts de contenu peuvent accéder aux API Chrome utilisées par leur extension parente en échangeant des messages avec l'extension. Elles peuvent également accéder à l'URL d'un fichier d'extension avec chrome.runtime.getURL() et utiliser le résultat de la même manière que les autres URL.

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

De plus, le script de contenu peut accéder directement aux API Chrome suivantes:

Les scripts de contenu ne peuvent pas accéder directement aux autres API.

Travailler dans des environnements isolés

Les scripts de contenu résident dans un environnement isolé, ce qui leur permet d'apporter des modifications à leur environnement JavaScript sans entrer en conflit avec la page ou les scripts de contenu supplémentaires.

Une extension peut s'exécuter sur une page Web avec un code semblable à l'exemple ci-dessous.

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

Cette extension peut injecter le script de contenu suivant.

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

Les deux alertes s'affichaient si vous cliquiez sur le bouton.

Les mondes isolés ne permettent pas aux scripts de contenu, à l'extension et à la page Web d'accéder aux variables ou aux fonctions créées par les autres. Cela permet également aux scripts de contenu d'activer des fonctionnalités qui ne doivent pas être accessibles à la page Web.

Injecter des scripts

Les scripts de contenu peuvent être injectés de manière programmatique ou déclarative.

Injecter par programmation

Utilisez l'injection programmatique pour les scripts de contenu qui doivent s'exécuter lors d'occasions spécifiques.

Pour injecter un script de contenu programmatique, indiquez l'autorisation activeTab dans le fichier manifeste. Fournit un accès sécurisé à l'hôte du site actif et un accès temporaire à l'autorisation tabs, ce qui permet au script de contenu de s'exécuter dans l'onglet actif actuel sans spécifier d'autorisations multi-origines.

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

Les scripts de contenu peuvent être injectés en tant que code.

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

Il est également possible d'injecter un fichier entier.

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

Injecter de manière déclarative

Utilisez l'injection déclarative pour les scripts de contenu qui doivent s'exécuter automatiquement sur les pages spécifiées.

Les scripts injectés de manière déclarative sont enregistrés dans le fichier manifeste sous le champ "content_scripts". Ils peuvent inclure des fichiers JavaScript, des fichiers CSS ou les deux. Tous les scripts de contenu exécutés automatiquement doivent spécifier des formats de correspondance.

{
 "name": "My extension",
 ...
 "content_scripts": [
   {
     "matches": ["http://*.nytimes.com/*"],
     "css": ["myStyles.css"],
     "js": ["contentScript.js"]
   }
 ],
 ...
}
Nom Type Description
matches {: #matches } tableau de chaînes Obligatoire. Indique les pages dans lesquelles ce script de contenu sera injecté. Consultez la section Formats de correspondance pour en savoir plus sur la syntaxe de ces chaînes, et la section Formats de correspondance et fichiers glob pour savoir comment exclure des URL.
css {: #css } tableau de chaînes Facultatif. Liste des fichiers CSS à injecter dans les pages correspondantes. Celles-ci sont injectées dans l'ordre dans lequel elles apparaissent dans ce tableau, avant la construction ou l'affichage des DOM sur la page.
js {: #js } tableau de chaînes Facultatif. Liste des fichiers JavaScript à injecter dans les pages correspondantes. Celles-ci sont injectées dans l'ordre dans lequel elles apparaissent dans ce tableau.
match_about_blank {: #match_about_blank } boolean Facultatif. Indique si le script doit être injecté dans un frame about:blank où le frame parent ou ouvre correspond à l'un des modèles déclarés dans matches. La valeur par défaut est false.

Exclure les correspondances et les globs

Vous pouvez personnaliser la correspondance des pages spécifiée en incluant les champs suivants dans l'enregistrement du fichier manifeste.

Nom Type Description
exclude_matches {: #excluded_matches } tableau de chaînes Facultatif. Exclut les pages dans lesquelles ce script de contenu serait autrement injecté. Consultez la section Formats de correspondance pour en savoir plus sur la syntaxe de ces chaînes.
include_globs {: #include_globs } tableau de chaînes Facultatif. S'applique après matches pour n'inclure que les URL qui correspondent également à ce glob. Destiné à émuler le mot clé Greasemonkey @include.
exclude_globs {: #excluded_globs } tableau de chaînes Facultatif. Appliqué après matches pour exclure les URL qui correspondent à ce glob. Destiné à émuler le mot clé @excludeGreasemonkey.

Le script de contenu est injecté dans une page si son URL correspond à un format matches ou include_globs, à condition qu'elle ne corresponde pas également à un format exclude_matches ou exclude_globs.

La propriété matches étant obligatoire, exclude_matches, include_globs et exclude_globs ne peuvent être utilisés que pour limiter les pages affectées.

L'extension suivante injectait le script de contenu dans http://www.nytimes.com/ health, mais pas dans http://www.nytimes.com/ business .

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

Les propriétés glob suivent une syntaxe différente et plus flexible que les modèles de correspondance. Les chaînes glob acceptées sont des URL qui peuvent contenir des astérisques et des points d'interrogation. L'astérisque * correspond à n'importe quelle chaîne de n'importe quelle longueur, y compris la chaîne vide, tandis que le point d'interrogation ? correspond à n'importe quel caractère.

Par exemple, le tag glob http:// ??? .example.com/foo/ * correspond à l'un des éléments suivants:

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

Toutefois, elle ne correspond pas aux éléments suivants:

  • http:// mon .example.com/foo/bar
  • http:// exemple .com/foo/
  • http://www.example.com/foo

Cette extension injecte le script de contenu dans http:/www.nytimes.com/ arts /index.html et http://www.nytimes.com/ jobs /index.html, mais pas dans http://www.nytimes.com/ sports/index.html.

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

Cette extension injecte le script de contenu dans http:// history .nytimes.com et http://.nytimes.com/ history, mais pas dans http:// science .nytimes.com ni dans http://www.nytimes.com/ science .

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

Un, tous ou certains d'entre eux peuvent être inclus pour atteindre le champ d'application correct.

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

Durée de diffusion

Le champ run_at permet de contrôler l'injection de fichiers JavaScript dans la page Web. Le champ privilégié par défaut est "document_idle", mais peut également être spécifié en tant que "document_start" ou "document_end" si nécessaire.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "run_at": "document_idle",
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Nom Type Description
document_idle {: #document_idle } chaîne Préféré. Utilisez "document_idle" dans la mesure du possible.

Le navigateur choisit une heure pour l'injection des scripts entre le "document_end" et immédiatement après le déclenchement de l'événement windowonload. Le moment exact de l'injection dépend de la complexité du document et du temps de chargement. Il est optimisé pour la vitesse de chargement des pages.

Les scripts de contenu exécutés à "document_idle" n'ont pas besoin d'écouter l'événement window.onload. Ils s'exécutent une fois le DOM terminé. Si un script doit absolument s'exécuter après window.onload, l'extension peut vérifier si onload s'est déjà déclenché à l'aide de la propriété document.readyState.
document_start {: #document_start } chaîne Les scripts sont injectés après n'importe quel fichier de css, mais avant la construction d'un autre DOM ou l'exécution de tout autre script.
document_end {: #document_end } chaîne Les scripts sont injectés immédiatement après la fin du DOM, mais avant le chargement des sous-ressources telles que les images et les cadres.

Spécifier des cadres

Le champ "all_frames" permet à l'extension d'indiquer si les fichiers JavaScript et CSS doivent être injectés dans tous les frames correspondant aux exigences d'URL spécifiées ou uniquement dans le frame supérieur d'un onglet.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "all_frames": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Nom Type Description
all_frames {: #all_frames } boolean Facultatif. La valeur par défaut est false, ce qui signifie que seul le frame supérieur est mis en correspondance.

Si la valeur true est spécifiée, la valeur est injectée dans tous les frames, même si celui-ci n'est pas le cadre supérieur de l'onglet. Chaque frame est vérifié indépendamment pour vérifier qu'il respecte les exigences liées aux URL. Il n'est pas injecté dans les frames enfants si ces exigences ne sont pas remplies.

Communication avec la page d'intégration

Bien que les environnements d'exécution des scripts de contenu et les pages qui les hébergent soient isolés les uns des autres, ils partagent l'accès au DOM de la page. Si la page souhaite communiquer avec le script de contenu, ou avec l'extension via le script de contenu, elle doit le faire via le DOM partagé.

Par exemple, vous pouvez utiliser 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 page sans extension, example.html, envoie des messages à elle-même. Ce message est intercepté et inspecté par le script de contenu, puis publié dans le processus d'extension. De cette manière, la page établit une ligne de communication avec le processus d'extension. L'inverse est possible par des moyens similaires.

Bénéficiez d'une sécurité optimale

Alors que les mondes isolés offrent un niveau de protection, l'utilisation de scripts de contenu peut créer des failles dans une extension et la page Web. Si le script de contenu reçoit du contenu d'un site Web distinct, par exemple en effectuant une requête XMLHttpRequest, veillez à filtrer les attaques de script intersites avant de l'injecter. Communiquez uniquement via HTTPS pour éviter les attaques "man-in-the-middle".

Veillez à filtrer les pages Web malveillantes. Par exemple, les formats suivants sont dangereux:

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

Privilégiez plutôt des API plus sûres qui n'exécutent pas de 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);