Skrypty dotyczące zawartości

Skrypty treści to pliki, które działają w kontekście stron internetowych. Korzystając ze standardowego obiektowego modelu dokumentu (DOM), mogą odczytywać szczegóły stron internetowych odwiedzanych przez przeglądarkę, wprowadzać w nich zmiany i przekazywać informacje do rozszerzenia nadrzędnego.

Możliwości skryptów treści

Skrypty treści mogą uzyskiwać dostęp do interfejsów API Chrome używanych przez rozszerzenie nadrzędne, wymieniając z nim wiadomości. Mogą też uzyskać dostęp do adresu URL pliku rozszerzenia za pomocą chrome.runtime.getURL() i używać wyniku tak samo jak innych adresów URL.

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

Dodatkowo skrypt treści może bezpośrednio uzyskiwać dostęp do tych interfejsów API Chrome:

Skrypty treści nie mogą bezpośrednio uzyskiwać dostępu do innych interfejsów API.

Praca w izolowanych światach

Skrypty dotyczące zawartości działają w izolowanym środowisku, co pozwala im wprowadzać zmiany w środowisku JavaScript bez powodowania konfliktów ze stroną lub dodatkowymi skryptami dotyczącymi zawartości.

Rozszerzenie może działać na stronie internetowej z kodem podobnym do tego w przykładzie poniżej.

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

To rozszerzenie może wstrzyknąć ten skrypt dotyczący zawartości:

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

Po naciśnięciu przycisku pojawią się oba alerty.

Odizolowane światy nie pozwalają skryptom treści, rozszerzeniu ani stronie internetowej na dostęp do żadnych zmiennych ani funkcji utworzonych przez inne elementy. Dzięki temu skrypty treści mogą włączać funkcje, które nie powinny być dostępne na stronie internetowej.

Wstrzykiwanie skryptów

Skrypty treści można wstrzykiwać programowo lub deklaratywnie.

Wstrzykiwanie programowe

Używaj wstrzykiwania programowego w przypadku skryptów treści, które muszą być uruchamiane w określonych sytuacjach.

Aby wstrzyknąć skrypt treści, w pliku manifestu podaj uprawnienie activeTab. Zapewnia to bezpieczny dostęp do hosta aktywnej witryny i tymczasowy dostęp do uprawnień kart, dzięki czemu skrypt treści może działać na bieżącej aktywnej karcie bez określania uprawnień do różnych domen.

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

Skrypty treści można wstrzykiwać jako kod.

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

Można też wstawić cały plik.

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

Wstrzykiwanie deklaratywne

Używaj wstrzykiwania deklaratywnego w przypadku skryptów treści, które mają być automatycznie uruchamiane na określonych stronach.

Skrypty wstrzykiwane deklaratywnie są rejestrowane w pliku manifestu w polu "content_scripts". Mogą one zawierać pliki JavaScript, pliki CSS lub oba te rodzaje plików. Wszystkie skrypty treści uruchamiane automatycznie muszą określać wzorce dopasowania.

{
 "name": "My extension",
 ...
 "content_scripts": [
   {
     "matches": ["http://*.nytimes.com/*"],
     "css": ["myStyles.css"],
     "js": ["contentScript.js"]
   }
 ],
 ...
}
Nazwa Typ Opis
matches {: #matches } tablica ciągów tekstowych Wymagane. Określa, na których stronach ma zostać wstrzyknięty ten skrypt treści. Więcej informacji o składni tych ciągów znaków znajdziesz w artykule Wzorce dopasowania, a informacje o wykluczaniu adresów URL – w artykule Wzorce dopasowania i wieloznaki.
css {: #css } tablica ciągów tekstowych Opcjonalnie. Lista plików CSS, które mają być wstrzykiwane na pasujące strony. Są one wstrzykiwane w kolejności, w jakiej występują w tej tablicy, zanim zostanie utworzony lub wyświetlony jakikolwiek DOM strony.
js {: #js } tablica ciągów tekstowych Opcjonalnie. Lista plików JavaScript, które mają być wstrzykiwane na pasujące strony. Są one wstrzykiwane w kolejności, w jakiej występują w tej tablicy.
match_about_blank {: #match_about_blank } Wartość logiczna Opcjonalnie. Określa, czy skrypt ma być wstrzykiwany do ramki about:blank, w której ramka nadrzędna lub otwierająca pasuje do jednego z wzorców zadeklarowanych w matches. Domyślna wartość to false.

Wykluczanie dopasowań i wzorców

Dopasowywanie określonej strony można dostosować, uwzględniając w rejestracji pliku manifestu te pola:

Nazwa Typ Opis
exclude_matches {: #exclude_matches } tablica ciągów tekstowych Opcjonalnie. Wyklucza strony, na których w innych okolicznościach zostałby wstrzyknięty ten skrypt treści. Więcej informacji o składni tych ciągów znaków znajdziesz w artykule Wzorce dopasowania.
include_globs {: #include_globs } tablica ciągów tekstowych Opcjonalnie. Stosowany po znaku matches, aby uwzględniać tylko te adresy URL, które pasują też do tego wzorca. Ma naśladować słowo kluczowe @include Greasemonkey.
exclude_globs {: #exclude_globs } tablica ciągów tekstowych Opcjonalnie. Stosowane po matches, aby wykluczyć adresy URL pasujące do tego wzorca. Ma naśladować słowo kluczowe @excludeGreasemonkey.

Skrypt treści zostanie wstrzyknięty na stronę, jeśli jej adres URL pasuje do dowolnego wzorca matches i dowolnego wzorca include_globs, o ile nie pasuje też do wzorca exclude_matches ani exclude_globs.

Właściwość matches jest wymagana, więc exclude_matches, include_globsexclude_globs można używać tylko do ograniczania liczby stron, na których będą widoczne zmiany.

Poniższe rozszerzenie wstrzyknie skrypt treści do strony http://www.nytimes.com/ health, ale nie do strony http://www.nytimes.com/ business .

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

Właściwości glob mają inną, bardziej elastyczną składnię niż wzorce dopasowania. Dopuszczalne ciągi glob to adresy URL, które mogą zawierać gwiazdki i znaki zapytania pełniące rolę symboli wieloznacznych. Gwiazdka * odpowiada dowolnemu ciągowi znaków o dowolnej długości, w tym pustemu ciągowi znaków, a znak zapytania ? dopasowuje dowolny pojedynczy znak.

Na przykład glob http:// ??? .example.com/foo/ * pasuje do dowolnego z tych adresów URL:

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

Nie pasuje jednak do tych wyrażeń:

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

To rozszerzenie wstrzyknie skrypt dotyczący zawartości na stronach http:/www.nytimes.com/ arts /index.htmlhttp://www.nytimes.com/ jobs /index.html, ale nie na stronie http://www.nytimes.com/ sports/index.html.

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

To rozszerzenie wstrzyknie skrypt treści do http:// history .nytimes.comhttp://.nytimes.com/ history, ale nie do http:// science .nytimes.com ani http://www.nytimes.com/ science .

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

Aby uzyskać odpowiedni zakres, możesz uwzględnić wszystkie te elementy, niektóre z nich lub tylko jeden z nich.

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

Czas trwania

Moment wstrzyknięcia plików JavaScript do strony internetowej jest kontrolowany przez pole run_at. Preferowanym i domyślnym polem jest "document_idle", ale w razie potrzeby można też użyć pól "document_start" lub "document_end".

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "run_at": "document_idle",
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Nazwa Typ Opis
document_idle {: #document_idle } ciąg znaków Preferowana. W miarę możliwości używaj "document_idle".

Przeglądarka wybiera czas na wstrzyknięcie skryptów między "document_end" a momentem bezpośrednio po uruchomieniu zdarzenia windowonload. Dokładny moment wstrzyknięcia zależy od złożoności dokumentu i czasu jego wczytywania oraz jest zoptymalizowany pod kątem szybkości wczytywania strony.

Skrypty treści działające w momencie "document_idle" nie muszą nasłuchiwać zdarzenia window.onload – gwarantujemy, że zostaną uruchomione po zakończeniu wczytywania DOM. Jeśli skrypt musi być uruchomiony po zdarzeniu window.onload, rozszerzenie może sprawdzić, czy zdarzenie onload zostało już wywołane, za pomocą właściwości document.readyState.
document_start {: #document_start } ciąg znaków Skrypty są wstrzykiwane po wszystkich plikach z css, ale przed utworzeniem jakiegokolwiek innego modelu DOM lub uruchomieniem innego skryptu.
document_end {: #document_end } ciąg znaków Skrypty są wstrzykiwane natychmiast po zakończeniu wczytywania DOM, ale przed załadowaniem zasobów podrzędnych, takich jak obrazy i ramki.

Określanie ramek

Pole "all_frames" pozwala rozszerzeniu określić, czy pliki JavaScript i CSS mają być wstrzykiwane do wszystkich ramek pasujących do określonych wymagań dotyczących adresu URL, czy tylko do ramki najwyższego poziomu na karcie.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "all_frames": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Nazwa Typ Opis
all_frames {: #all_frames } Wartość logiczna Opcjonalnie. Domyślnie jest to false, co oznacza, że dopasowywana jest tylko górna ramka.

Jeśli podasz wartość true, kod zostanie wstrzyknięty do wszystkich ramek, nawet jeśli nie jest to ramka najwyższego poziomu na karcie. Każda ramka jest sprawdzana niezależnie pod kątem wymagań dotyczących adresu URL. Jeśli wymagania nie są spełnione, nie będzie ona wstawiana do ramek podrzędnych.

Komunikacja ze stroną, na której jest osadzony

Środowiska wykonawcze skryptów treści i stron, które je hostują, są od siebie odizolowane, ale mają wspólny dostęp do DOM strony. Jeśli strona chce komunikować się ze skryptem treści lub z rozszerzeniem za jego pomocą, musi to robić za pomocą wspólnego DOM.

Przykład można uzyskać za pomocą funkcji 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);

Strona bez rozszerzenia, example.html, wysyła do siebie wiadomości. Ta wiadomość jest przechwytywana i sprawdzana przez skrypt treści, a następnie wysyłana do procesu rozszerzenia. W ten sposób strona nawiązuje komunikację z procesem rozszerzenia. Można to zrobić w odwrotną stronę za pomocą podobnych środków.

Zadbaj o bezpieczeństwo

Chociaż odizolowane światy zapewniają pewną ochronę, używanie skryptów dotyczących zawartości może powodować luki w zabezpieczeniach rozszerzenia i strony internetowej. Jeśli skrypt treści otrzymuje treści z innej witryny, np. wykonując XMLHttpRequest, przed wstrzyknięciem treści należy je dokładnie przefiltrować, aby zapobiec atakom typu cross-site scripting. Komunikuj się tylko za pomocą protokołu HTTPS, aby uniknąć ataków typu "man-in-the-middle".

Pamiętaj, aby filtrować złośliwe strony internetowe. Na przykład te wzorce są niebezpieczne:

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

Zamiast tego używaj bezpieczniejszych interfejsów API, które nie uruchamiają skryptów:

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