Inhoudsscripts

Contentscripts zijn bestanden die worden uitgevoerd in de context van webpagina's. Door gebruik te maken van het standaard Document Object Model (DOM) kunnen ze details lezen van de webpagina's die de browser bezoekt, er wijzigingen in aanbrengen en informatie doorgeven aan de bijbehorende extensie.

Inzicht in de mogelijkheden van content scripts

Scripts met specifieke inhoud kunnen toegang krijgen tot de Chrome API's die door hun bovenliggende extensie worden gebruikt door berichten met de extensie uit te wisselen. Ze kunnen ook de URL van een extensiebestand opvragen met chrome.runtime.getURL() en het resultaat op dezelfde manier gebruiken als andere URL's.

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

Daarnaast heeft content script rechtstreeks toegang tot de volgende Chrome API's:

Contentscripts hebben geen directe toegang tot andere API's.

Werken in geïsoleerde werelden

Contentscripts bevinden zich in een geïsoleerde omgeving, waardoor een contentscript wijzigingen kan aanbrengen in zijn JavaScript-omgeving zonder conflicten te veroorzaken met de pagina of andere contentscripts.

Een extensie kan in een webpagina worden uitgevoerd met code die vergelijkbaar is met het onderstaande voorbeeld.

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

Die extensie zou het volgende inhoudsscript kunnen injecteren.

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

Beide meldingen zouden verschijnen als de knop werd ingedrukt.

Geïsoleerde werelden staan ​​niet toe dat content scripts, de extensie en de webpagina toegang krijgen tot variabelen of functies die door anderen zijn aangemaakt. Dit geeft content scripts ook de mogelijkheid om functionaliteit in te schakelen die niet toegankelijk zou moeten zijn voor de webpagina.

Scripts injecteren

Contentscripts kunnen programmatisch of declaratief worden ingevoegd.

Injecteer programmatisch

Gebruik programmatische injectie voor scripts die op specifieke momenten moeten worden uitgevoerd.

Om een ​​programmatisch contentscript te injecteren, moet u de `activeTab`- machtiging in het manifest opnemen. Dit verleent beveiligde toegang tot de host van de actieve site en tijdelijke toegang tot de `tabs`- machtiging, waardoor het contentscript op het huidige actieve tabblad kan worden uitgevoerd zonder dat er cross-origin-machtigingen hoeven te worden gespecificeerd.

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

Scriptinhoud kan als code worden ingevoegd.

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

Of er kan een volledig bestand worden geïnjecteerd.

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

Declaratief injecteren

Gebruik declaratieve injectie voor contentscripts die automatisch op specifieke pagina's moeten worden uitgevoerd.

Scripts die declaratief worden geïnjecteerd, worden geregistreerd in het manifest onder het veld "content_scripts" . Dit kunnen JavaScript-bestanden, CSS-bestanden of beide zijn. Alle automatisch uitgevoerde contentscripts moeten matchpatronen specificeren.

{
 "name": "My extension",
 ...
 "content_scripts": [
   {
     "matches": ["http://*.nytimes.com/*"],
     "css": ["myStyles.css"],
     "js": ["contentScript.js"]
   }
 ],
 ...
}
Naam Type Beschrijving
matches {: #matches } array van strings Vereist. Specificeert in welke pagina's dit contentscript wordt geïnjecteerd. Zie Matchpatronen voor meer informatie over de syntaxis van deze tekenreeksen en Matchpatronen en globs voor informatie over het uitsluiten van URL's.
css {: #css } array van strings Optioneel. De lijst met CSS-bestanden die in de overeenkomende pagina's moeten worden geïnjecteerd. Deze worden geïnjecteerd in de volgorde waarin ze in deze lijst voorkomen, voordat er DOM voor de pagina wordt opgebouwd of weergegeven.
js {: #js } array van strings Optioneel. De lijst met JavaScript-bestanden die in de overeenkomende pagina's moeten worden geïnjecteerd. Deze worden geïnjecteerd in de volgorde waarin ze in deze array voorkomen.
match_about_blank {: #match_about_blank } booleaans Optioneel. Geeft aan of het script moet worden geïnjecteerd in een about:blank frame waarvan het ouder- of openingsframe overeenkomt met een van de patronen die zijn gedeclareerd in matches . Standaardwaarde is false .

Sluit matches en globs uit.

De specifieke paginamatching kan worden aangepast door de volgende velden in de manifestregistratie op te nemen.

Naam Type Beschrijving
exclude_matches {: #exclude_matches } array van strings Optioneel. Sluit pagina's uit waar dit contentscript anders zou worden ingevoegd. Zie 'Overeenkomstpatronen' voor meer informatie over de syntaxis van deze tekenreeksen.
include_globs {: #include_globs } array van strings Optioneel. Wordt toegepast na matches om alleen die URL's op te nemen die ook overeenkomen met deze glob. Bedoeld om het @include Greasemonkey-trefwoord na te bootsen.
exclude_globs {: #exclude_globs } array van strings Optioneel. Wordt toegepast na matches om URL's uit te sluiten die overeenkomen met deze glob. Bedoeld om het Greasemonkey-trefwoord @exclude na te bootsen.

Het contentscript wordt in een pagina geïnjecteerd als de URL overeenkomt met een van matches patronen en een van include_globs patronen, zolang de URL niet ook overeenkomt met een exclude_matches of exclude_globs patroon.

Omdat de eigenschap matches verplicht is, kunnen exclude_matches , include_globs en exclude_globs alleen worden gebruikt om te beperken welke pagina's worden beïnvloed.

De volgende extensie zou het contentscript injecteren in http://www.nytimes.com/health , maar niet in http://www.nytimes.com/business .

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

Glob-eigenschappen volgen een andere, flexibelere syntaxis dan match-patronen . Acceptabele glob-strings zijn URL's die "jokertekens" zoals asterisken en vraagtekens kunnen bevatten. De asterisk * komt overeen met elke string van elke lengte, inclusief een lege string, terwijl het vraagteken ? overeenkomt met elk willekeurig teken.

De glob http://???.example.com/foo/* komt bijvoorbeeld overeen met elk van de volgende:

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

Het komt echter niet overeen met het volgende:

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

Deze extensie zou het contentscript injecteren in http:/www.nytimes.com/arts/index.html en http://www.nytimes.com/jobs/index.html , maar niet in http://www.nytimes.com/sports/index.html .

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

Deze extensie zou het contentscript injecteren in http://history.nytimes.com en http://.nytimes.com/history , maar niet in http://science.nytimes.com of http://www.nytimes.com/science .

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

Een, alle of een deel van deze elementen kunnen worden opgenomen om de juiste reikwijdte te bereiken.

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

Looptijd

Wanneer JavaScript-bestanden in de webpagina worden geïnjecteerd, wordt dit bepaald door het veld run_at . Het standaardveld is "document_idle" , maar indien nodig kan ook "document_start" of "document_end" worden opgegeven.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "run_at": "document_idle",
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Naam Type Beschrijving
document_idle {: #document_idle } snaar Voorkeur. Gebruik "document_idle" waar mogelijk.

De browser kiest een moment om scripts te injecteren tussen "document_end" en direct na het afvuren van de windowonload gebeurtenis. Het exacte injectiemoment hangt af van de complexiteit van het document en de laadtijd, en is geoptimaliseerd voor een snelle laadtijd van de pagina.

Contentscripts die worden uitgevoerd in de status "document_idle" hoeven niet te luisteren naar de window.onload -gebeurtenis; ze worden gegarandeerd uitgevoerd nadat de DOM is voltooid. Als een script absoluut na window.onload moet worden uitgevoerd, kan de extensie controleren of onload al is geactiveerd door de eigenschap document.readyState te gebruiken.
document_start {: #document_start } snaar Scripts worden geïnjecteerd na alle css bestanden, maar vóórdat andere DOM-elementen worden opgebouwd of andere scripts worden uitgevoerd.
document_end {: #document_end } snaar Scripts worden direct na de volledige DOM-opbouw geïnjecteerd, maar voordat subbronnen zoals afbeeldingen en frames zijn geladen.

Specificeer frames

Met het veld "all_frames" kan de extensie specificeren of JavaScript- en CSS-bestanden in alle frames die aan de opgegeven URL-vereisten voldoen, of alleen in het bovenste frame van een tabblad, moeten worden geïnjecteerd.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "all_frames": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Naam Type Beschrijving
all_frames {: #alle frames } booleaans Optioneel. Standaardwaarde is false , wat betekent dat alleen het bovenste frame wordt vergeleken.

Indien ingesteld op true , wordt de code in alle frames geïnjecteerd, zelfs als het frame niet het bovenste frame in het tabblad is. Elk frame wordt afzonderlijk gecontroleerd op URL-vereisten; er wordt geen code in onderliggende frames geïnjecteerd als niet aan de URL-vereisten wordt voldaan.

Communicatie met de inbeddingspagina

Hoewel de uitvoeringsomgevingen van contentscripts en de pagina's waarop ze worden gehost van elkaar gescheiden zijn, delen ze de toegang tot de DOM van de pagina. Als de pagina met het contentscript of met de extensie via het contentscript wil communiceren, moet dit via de gedeelde DOM gebeuren.

Een voorbeeld hiervan kan worden bereikt met behulp van 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);

De pagina example.html, die geen onderdeel uitmaakt van de extensie, verstuurt berichten naar zichzelf. Deze berichten worden onderschept en gecontroleerd door het content-script en vervolgens doorgestuurd naar het extensieproces. Op deze manier legt de pagina een communicatielijn met het extensieproces. Het omgekeerde is op vergelijkbare wijze mogelijk.

Blijf veilig

Hoewel geïsoleerde werelden een beschermingslaag bieden, kan het gebruik van contentscripts kwetsbaarheden creëren in een extensie en de webpagina. Als het contentscript content ontvangt van een aparte website, bijvoorbeeld via een XMLHttpRequest , zorg er dan voor dat u de content filtert op cross-site scripting- aanvallen voordat u deze injecteert. Communiceer uitsluitend via HTTPS om man-in-the-middle- aanvallen te voorkomen.

Zorg ervoor dat u schadelijke webpagina's filtert. De volgende patronen zijn bijvoorbeeld gevaarlijk:

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

Geef in plaats daarvan de voorkeur aan veiligere API's die geen scripts uitvoeren:

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