Scripts de contenu

Les scripts de contenu sont des fichiers exécutés dans le contexte des pages Web. En utilisant le modèle objet de document (DOM) standard, elles peuvent lire les détails des pages Web que le navigateur consulte, 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. Ils peuvent également accéder à l'URL du fichier d'une 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 à d'autres API.

Travailler dans des mondes isolés

Les scripts de contenu évoluent dans un monde isolé, ce qui leur permet de modifier leur environnement JavaScript sans entrer en conflit avec la page ou d'autres scripts de contenu.

Une extension peut s'exécuter dans 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);

Si le bouton était enfoncé, les deux alertes s'afficheraient.

Les mondes isolés n'autorisent pas les scripts de contenu, l'extension et la page Web à accéder à des variables ou à des 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 de manière programmatique

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

Pour injecter un script de contenu programmatique, fournissez l'autorisation activeTab dans le fichier manifeste. Cela permet d'accorder 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 d'être exécuté 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"'
      });
    }
  });

Un fichier entier peut également être injecté.

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 être exécutés automatiquement sur des 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écution automatique doivent spécifier des modèles 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é. Pour en savoir plus sur la syntaxe de ces chaînes, consultez Formats de correspondance. Pour savoir comment exclure des URL, consultez Formats de correspondance et expressions génériques.
css {: #css } tableau de chaînes Facultatif. Liste des fichiers CSS à injecter dans les pages correspondantes. Ils sont injectés dans l'ordre dans lequel ils apparaissent dans ce tableau, avant qu'un DOM ne soit construit ou affiché pour la page.
js {: #js } tableau de chaînes Facultatif. Liste des fichiers JavaScript à injecter dans les pages correspondantes. Ils sont injectés dans l'ordre dans lequel ils apparaissent dans ce tableau.
match_about_blank {: #match_about_blank } booléen Facultatif. Indique si le script doit être injecté dans un frame about:blank où le frame parent ou d'ouverture correspond à l'un des modèles déclarés dans matches. La valeur par défaut est false.

Exclure les correspondances et les expressions régulières

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

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

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

Étant donné que la propriété matches est obligatoire, exclude_matches, include_globs et exclude_globs ne peuvent être utilisés que pour limiter les pages concernées.

L'extension suivante injecterait 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 pouvant contenir des astérisques et des points d'interrogation "génériques". L'astérisque * remplace 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, l'expression générique http:// ??? .example.com/foo/ * correspond à l'une des expressions suivantes:

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

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

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

Cette extension injecterait 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 injecterait le script de contenu dans http:// history .nytimes.com et http://.nytimes.com/ history, mais pas dans 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"]
    }
  ],
  ...
}

Vous pouvez inclure l'un, tous ou certains de ces éléments pour obtenir la portée appropriée.

{
  "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 moment où les fichiers JavaScript sont injectés dans la page Web est contrôlé par le champ run_at. Le champ préféré et par défaut est "document_idle", mais il peut également être spécifié comme "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 Recommandé. Utilisez "document_idle" dans la mesure du possible.

Le navigateur choisit un moment pour injecter des scripts entre "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 de la durée de son chargement. Il est optimisé pour la vitesse de chargement de la page.

Les scripts de contenu exécutés à "document_idle" n'ont pas besoin d'écouter l'événement window.onload. Ils s'exécutent toujours après la fin du DOM. 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 les fichiers de css, mais avant la création de tout 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 finalisation 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 de spécifier si les fichiers JavaScript et CSS doivent être injectés dans tous les cadres correspondant aux exigences d'URL spécifiées ou uniquement dans le cadre le plus élevé 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 } booléen Facultatif. La valeur par défaut est false, ce qui signifie que seul le frame supérieur est mis en correspondance.

Si vous spécifiez true, l'injection se produit dans tous les frames, même s'ils ne sont pas le frame le plus élevé de l'onglet. Les exigences concernant les URL sont vérifiées indépendamment pour chaque frame. Elles ne seront pas injectées dans les frames enfants si elles ne sont pas respectées.

Communication avec la page d'intégration

Bien que les environnements d'exécution des scripts de contenu et des 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é.

Vous pouvez par exemple 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, publie des messages sur 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

Bien que les mondes isolés fournissent une couche de protection, l'utilisation de scripts de contenu peut créer des failles dans une extension et une page Web. Si le script de contenu reçoit du contenu à partir d'un autre site Web, par exemple en créant une XMLHttpRequest, veillez à filtrer les attaques de scripts intersites avant de les injecter. Ne communiquez que via HTTPS pour éviter les attaques "man-in-the-middle".

Veillez à filtrer les pages Web malveillantes. Par exemple, les modèles 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écurisées 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);