Inhoudsscripts

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

Begrijp de mogelijkheden van inhoudsscripts

Contentscripts hebben toegang tot Chrome API's die door de bovenliggende extensie worden gebruikt door berichten uit te wisselen met de extensie. Ze hebben ook toegang tot de URL van een extensiebestand met chrome.runtime.getURL() en kunnen 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;

Bovendien heeft het inhoudsscript rechtstreeks toegang tot de volgende Chrome-API's:

Inhoudsscripts hebben geen directe toegang tot andere API's.

Werk in geïsoleerde werelden

Inhoudsscripts leven in een geïsoleerde wereld, waardoor een inhoudsscript wijzigingen kan aanbrengen in de JavaScript-omgeving zonder conflicten met de pagina of aanvullende inhoudsscripts.

Een extensie kan op een webpagina worden uitgevoerd met code die lijkt op 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 waarschuwingen zouden verschijnen als de knop werd ingedrukt.

In geïsoleerde werelden is het niet mogelijk dat inhoudsscripts, de extensie en de webpagina toegang krijgen tot variabelen of functies die door anderen zijn gemaakt. Dit geeft inhoudsscripts ook de mogelijkheid om functionaliteit in te schakelen die niet toegankelijk zou moeten zijn voor de webpagina.

Scripts injecteren

Inhoudsscripts kunnen programmatisch of declaratief worden geïnjecteerd.

Programmatisch injecteren

Gebruik programmatische injectie voor contentscripts die bij specifieke gelegenheden moeten worden uitgevoerd.

Als u een programmatisch inhoudsscript wilt injecteren, geeft u de activeTab- machtiging op in het manifest. Dit verleent veilige toegang tot de host van de actieve site en tijdelijke toegang tot de tabbladen , waardoor het inhoudsscript op het huidige actieve tabblad kan worden uitgevoerd zonder cross-origin-machtigingen op te geven.

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

Contentscripts kunnen als code worden geïnjecteerd.

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

Of er kan een heel 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 inhoudsscripts die automatisch op specifieke pagina's moeten worden uitgevoerd.

Declaratief geïnjecteerde scripts worden in het manifest geregistreerd onder het veld "content_scripts" . Ze kunnen JavaScript-bestanden, CSS-bestanden of beide bevatten. Alle automatisch uitgevoerde inhoudsscripts moeten overeenkomstpatronen specificeren.

{
 "name": "My extension",
 ...
 "content_scripts": [
   {
     "matches": ["http://*.nytimes.com/*"],
     "css": ["myStyles.css"],
     "js": ["contentScript.js"]
   }
 ],
 ...
}
Naam Type Beschrijving
matches {: #matches } reeks strings Vereist. Specificeert op welke pagina's dit inhoudsscript wordt geïnjecteerd. Zie Matchpatronen voor meer details over de syntaxis van deze tekenreeksen en Matchpatronen en globs voor informatie over het uitsluiten van URL's.
css {: #css} reeks strings Optioneel. De lijst met CSS-bestanden die in overeenkomende pagina's moeten worden geïnjecteerd. Deze worden geïnjecteerd in de volgorde waarin ze in deze array verschijnen, voordat er een DOM voor de pagina wordt gemaakt of weergegeven.
js {: #js } reeks strings Optioneel. De lijst met JavaScript-bestanden die in overeenkomende pagina's moeten worden geïnjecteerd. Deze worden geïnjecteerd in de volgorde waarin ze in deze array verschijnen.
match_about_blank {: #match_about_blank } Booleaans Optioneel. Of het script moet worden geïnjecteerd in een about:blank frame waarbij het bovenliggende of openerframe overeenkomt met een van de patronen die zijn gedeclareerd in matches . Standaard ingesteld op false .

Exclusief lucifers en klodders

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

Naam Type Beschrijving
exclude_matches {: #exclude_matches } reeks strings Optioneel. Exclusief pagina's waarin dit inhoudsscript anders zou worden geïnjecteerd. Zie Matchpatronen voor meer details over de syntaxis van deze tekenreeksen.
include_globs {: #include_globs } reeks strings Optioneel. Toegepast na matches om alleen die URL's op te nemen die ook overeenkomen met deze glob. Bedoeld om het trefwoord @include Greasemonkey te emuleren.
exclude_globs {: #exclude_globs } reeks tekenreeksen Optioneel. Toegepast na matches om URL's uit te sluiten die overeenkomen met deze glob. Bedoeld om het trefwoord @exclude Greasemonkey te emuleren.

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

Omdat de eigenschap matches vereist 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 inhoudsscript in http://www.nytimes.com/health injecteren, 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 matchpatronen . Acceptabele glob-tekenreeksen zijn URL's die 'wildcard'-sterretjes en vraagtekens kunnen bevatten. Het sterretje * komt overeen met elke tekenreeks van welke lengte dan ook, inclusief de lege tekenreeks, terwijl het vraagteken ? komt overeen met elk afzonderlijk teken.

Bijvoorbeeld de glob http:// ??? .example.com/foo/ * komt overeen met een van de volgende waarden:

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

Het komt echter niet overeen met het volgende:

  • http:// mijn .example.com/foo/bar
  • http:// voorbeeld .com/foo/
  • http://www.example.com/foo

Deze extensie injecteert het inhoudsscript in http://www.nytimes.com/arts/index.html en http://www.nytimes.com/jobs/index.html, maar niet in http://www.nytimes.com /sport/index.html .

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

Deze extensie injecteert het inhoudsscript in http:// history .nytimes.com en http://.nytimes.com/ history , maar niet in http://science .nytimes.com of http://www.nytimes.com / wetenschap .

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

Eén, alle of enkele hiervan 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 geregeld door het run_at veld. Het voorkeurs- en standaardveld is "document_idle" , maar kan indien nodig ook worden opgegeven als "document_start" of "document_end" .

{
  "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 waar mogelijk "document_idle" .

De browser kiest een tijdstip voor het injecteren van scripts tussen "document_end" en onmiddellijk nadat de windowonload gebeurtenis wordt geactiveerd. Het exacte moment van injectie hangt af van hoe complex het document is en hoe lang het duurt om te laden, en is geoptimaliseerd voor de laadsnelheid van de pagina.

Inhoudsscripts die worden uitgevoerd op "document_idle" hoeven niet te luisteren naar de gebeurtenis window.onload ; ze worden gegarandeerd uitgevoerd nadat de DOM is voltooid. Als een script zeker moet worden uitgevoerd na window.onload , kan de extensie controleren of onload al is geactiveerd door de eigenschap document.readyState te gebruiken.
document_start {: #document_start } snaar Scripts worden na bestanden uit css geïnjecteerd, maar voordat een andere DOM wordt geconstrueerd of een ander script wordt uitgevoerd.
document_end {: #document_end } snaar Scripts worden onmiddellijk geïnjecteerd nadat de DOM is voltooid, maar voordat subbronnen zoals afbeeldingen en frames zijn geladen.

Geef kaders op

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

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "all_frames": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Naam Type Beschrijving
all_frames {: #alle_frames } Booleaans Optioneel. Standaard ingesteld op false , wat betekent dat alleen het bovenste frame overeenkomt.

Indien true opgegeven, wordt het in alle frames geïnjecteerd, zelfs als het frame niet het bovenste frame op het tabblad is. Elk frame wordt onafhankelijk gecontroleerd op URL-vereisten. Het wordt niet in onderliggende frames geïnjecteerd als niet aan de URL-vereisten wordt voldaan.

Communicatie met de insluitingspagina

Hoewel de uitvoeringsomgevingen van inhoudsscripts en de pagina's die deze hosten van elkaar geïsoleerd zijn, delen ze de toegang tot de DOM van de pagina. Als de pagina wil communiceren met het inhoudsscript, of met de extensie via het inhoudsscript, moet dit gebeuren via de gedeelde DOM.

Een voorbeeld 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 niet-extensiepagina, example.html, plaatst berichten naar zichzelf. Dit bericht wordt onderschept en geïnspecteerd door het inhoudsscript en vervolgens in het extensieproces geplaatst. Op deze manier brengt de pagina een communicatielijn tot stand met het extensieproces. Het omgekeerde is mogelijk via soortgelijke middelen.

Blijf veilig

Hoewel geïsoleerde werelden een beschermingslaag bieden, kan het gebruik van inhoudsscripts kwetsbaarheden in een extensie en de webpagina veroorzaken. Als het inhoudsscript inhoud van een afzonderlijke website ontvangt, zoals het maken van een XMLHttpRequest , zorg er dan voor dat u cross-site scripting- aanvallen op inhoud filtert voordat u deze injecteert. Communiceer alleen via HTTPS om "man-in-the-middle" -aanvallen te voorkomen.

Zorg ervoor dat u filtert op kwaadaardige webpagina's. 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);