Scripts de contenu

Les scripts de contenu sont des fichiers qui s'exécutent dans le contexte de pages Web. En utilisant le Document Object Model (DOM) standard, ils peuvent lire les détails des pages Web que le navigateur visite, apporter des modifications 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 d'un fichier d'extension avec chrome.runtime.getURL() et utiliser le résultat comme d'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, les scripts de contenu peuvent 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 se trouvent dans un monde isolé, ce qui leur permet d'apporter des modifications à leur environnement JavaScript sans entrer en conflit avec la page ni avec d'autres scripts de contenu.

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'affichent si vous appuyez sur le bouton.

Les mondes isolés n'autorisent pas les scripts de contenu, l'extension et la page Web à 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 devraient 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 accorde un accès sécurisé à l'hôte du site actif et un accès temporaire à l'autorisation des onglets, ce qui permet au script de contenu de s'exécuter sur l'onglet actif actuel sans spécifier d'autorisations inter-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"'
      });
    }
  });

Vous pouvez également 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 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. Spécifie les pages dans lesquelles ce script de contenu sera injecté. Pour en savoir plus sur la syntaxe de ces chaînes, consultez la section Modèles de correspondance. Pour savoir comment exclure des URL, consultez la section Modèles de correspondance et globs.
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 la construction ou l'affichage d'un DOM 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 cadre about:blank où le cadre 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 globs

La correspondance de page spécifiée est personnalisable 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 la section Modèles 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 @include mot clé de Greasemonkey.
exclude_globs {: #exclude_globs } tableau de chaînes Facultatif. Appliqué après matches pour exclure les URL qui correspondent à ce glob. Destiné à émuler le @excludemot clé de Greasemonkey.

Le script de contenu est injecté dans une page si son URL correspond à un modèle matches et à un modèle include_globs, à condition qu'elle ne corresponde pas également à un modèle 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 injecte 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 acceptables sont des URL qui peuvent contenir des astérisques et des points d'interrogation "génériques". 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 glob http:// ??? .example.com/foo/ * correspond à l'un des éléments suivants :

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

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

  • http:// my .example.com/foo/bar
  • http:// example .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"]
    }
  ],
  ...
}

Vous pouvez inclure un, plusieurs ou tous 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 d'exécution

Le champ run_at contrôle le moment où les fichiers JavaScript sont injectés dans la page Web. 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 Préféré. Utilisez "document_idle" chaque fois que cela est possible.

Le navigateur choisit un moment pour injecter des scripts entre "document_end" et immédiatement après le windowonload événement se déclenche. Le moment exact de l'injection dépend de la complexité du document et du temps nécessaire à son chargement. Il est optimisé pour la vitesse de chargement de la page.

Les scripts de contenu exécutés sur "document_idle" n'ont pas besoin d'écouter l'événement window.onload. Ils sont garantis de s'exécuter une fois le DOM terminé. Si un script doit absolument s'exécuter après window.onload, l'extension peut vérifier si onload a déjà été déclenché à l'aide de la propriété document.readyState.
document_start {: #document_start } chaîne Les scripts sont injectés après tous les fichiers de css, mais avant la construction d'un autre DOM ou l'exécution d'un 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 de spécifier si les fichiers JavaScript et CSS doivent être injectés dans tous les cadres correspondant aux exigences relatives aux URL spécifiées ou uniquement dans le cadre le plus haut 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 cadre supérieur est mis en correspondance.

Si la valeur true est spécifiée, l'injection se fait dans tous les cadres, même si le cadre n'est pas le plus haut de l'onglet. Chaque cadre est vérifié indépendamment pour les exigences relatives aux URL. L'injection ne se fait pas dans les cadres enfants si les exigences relatives aux URL ne sont pas remplies.

Communiquer 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 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 non-extension, example.html, s'envoie des messages. Ce message est intercepté et inspecté par le script de contenu, puis envoyé au 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 offrent une couche 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 autre site Web, par exemple en effectuant une XMLHttpRequest, veillez à filtrer les attaques de script inter-sites avant de l'injecter. Communiquez uniquement via HTTPS pour éviter "man-in-the-middle" les attaques de l'intercepteur.

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

Préférez 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);