Skrypty treści

Skrypty treści to pliki, które działają w kontekście stron internetowych. Korzystając ze standardowego modelu obiektów 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 ich rozszerzenie nadrzędne, wymieniając się z nim wiadomościami. Mogą też uzyskać dostęp do adresu URL pliku rozszerzenia za pomocą chrome.runtime.getURL() i używać go 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 korzystać z tych interfejsów API Chrome:

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

Praca w odizolowanych światach

Skrypty dotyczące zawartości działają w odizolowanym środowisku, co pozwala im wprowadzać zmiany w swoim środowisku JavaScript bez konfliktu 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>

Rozszerzenie może wstrzyknąć następujący skrypt treś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.

Izolowane światy nie pozwalają skryptom treści, rozszerzeniom i stronimom internetowym na dostęp do żadnych zmiennych ani funkcji utworzonych przez inne światy. Daje to skryptom treści możliwość włączenia funkcji, które nie powinny być dostępne dla strony internetowej.

Wstrzykiwanie skryptów

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

Wstawianie za pomocą kodu

Do skryptów treści, które muszą być uruchamiane w określonych sytuacjach, używaj automatycznego wstrzykiwania.

Aby wstrzyknąć skrypt treści programowych, w pliku manifestu podaj uprawnienie activeTab. Daje to bezpieczny dostęp do hosta aktywnej witryny i tymczasowy dostęp do uprawnień tabs, co umożliwia uruchamianie skryptu treści na bieżącej aktywnej karcie bez konieczności określania uprawnień między domenami.

{
  "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żesz też wstrzyknąć cały plik.

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

deklaratywnie,

Do skryptów treści, które powinny być automatycznie uruchamiane na określonych stronach, użyj deklaratywnego wstrzyknięcia.

Skrypty wstrzyknięte deklaratywnie są rejestrowane w pliku manifestu w polu "content_scripts". Mogą to być pliki JavaScript lub CSS, a także oba te typy. Wszystkie skrypty treści uruchamiane automatycznie muszą zawierać wzorce dopasowania.

{
 "name": "My extension",
 ...
 "content_scripts": [
   {
     "matches": ["http://*.nytimes.com/*"],
     "css": ["myStyles.css"],
     "js": ["contentScript.js"]
   }
 ],
 ...
}
Nazwa Typ Opis
matches {: #matches } tabelka tekstowa Wymagany. Określa, na których stronach ma być wstrzykiwany skrypt treści. Więcej informacji o składni tych ciągów znaków znajdziesz w artykule Wzorce dopasowania, a o wykluczaniu adresów URL – w artykule Wzorce dopasowania i globy.
css {: #css } tabelka tekstowa Opcjonalnie. Lista plików CSS, które mają zostać wstrzyknięte na pasujące strony. Są one wstawiane w kolejności, w jakiej występują w tym tablicy, zanim DOM zostanie utworzony lub wyświetlony na stronie.
js {: #js } tabelka tekstowa Opcjonalnie. Lista plików JavaScript, które mają zostać wstrzyknięte na pasujące strony. Są one wstawiane w kolejności, w jakiej występują w tej tablicy.
match_about_blank {: #match_about_blank } wartość logiczna Opcjonalnie. Określa, czy skrypt ma zostać wstrzyknięty do elementu iframe about:blank, w którym element nadrzędny lub otwierający odpowiada jednemu z wzorów zadeklarowanych w elementach matches. Domyślna wartość to false.

Wykluczanie dopasowań i dopasowań nieprecyzyjnych

Dopasowywanie do określonej strony można dostosować, podając w pliku rejestracji pliku manifestu odpowiednie pola.

Nazwa Typ Opis
exclude_matches {: #exclude_matches } tabelka tekstowa Opcjonalnie. Wyklucza strony, na które skrypt treści miałby zostać wstrzyknięty. Więcej informacji o składni tych ciągów znajdziesz w artykule Wzorce dopasowania.
include_globs {: #include_globs } tabelka tekstowa Opcjonalnie. Stosuje się go po matches, aby uwzględnić tylko te adresy URL, które pasują do tego wyrażenia regularnego. Słowo kluczowe Greasemonkey @include ma naśladować działanie słowa kluczowego Greasemonkey.
exclude_globs {: #exclude_globs } tabelka tekstowa Opcjonalnie. Stosuje się go po matches, aby wykluczyć adresy URL pasujące do tego wzorca. Słowo kluczowe Greasemonkey @exclude ma naśladować działanie słowa kluczowego Greasemonkey.

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 adres URL nie pasuje do wzorca exclude_matches ani exclude_globs.

Właściwość matches jest wymagana, więc atrybuty exclude_matches, include_globsexclude_globs można używać tylko do ograniczania zakresu stron, na których mają one obowiązywać.

Podane niżej rozszerzenie wstrzykuje skrypt treści do witryny http://www.nytimes.com/ health, ale nie do witryny http://www.nytimes.com/ business .

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

Właściwości globa mają inną, bardziej elastyczną składnię niż wzorce dopasowania. Dopuszczalne ciągi tekstowe to adresy URL, które mogą zawierać „symbole wieloznaczne” w postaci gwiazdek i znaków zapytania. Gwiazdka * pasuje do dowolnego ciągu o dowolnej długości, w tym do pustego ciągu, podczas gdy znak zapytania ? dopasowuje dowolny pojedynczy znak.

Na przykład wyrażenie regularne http:// ??? .example.com/foo/ * pasuje do dowolnego z tych elementów:

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

Nie odpowiada jednak:

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

To rozszerzenie wstrzykuje skrypt treści do http:/www.nytimes.com/ arts /index.htmlhttp://www.nytimes.com/ jobs /index.html, ale nie do http://www.nytimes.com/ sports/index.html.

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

Rozszerzenie wstrzykuje skrypt treści do http:// history .nytimes.com i http://.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ć jeden, wszystkie lub niektóre z tych elementów.

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

Wstawianie plików JavaScriptu na stronie internetowej jest kontrolowane przez pole run_at. Pole preferowane i domyślne to "document_idle", ale w razie potrzeby można też użyć wartości "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 tagu "document_idle".

Przeglądarka wybiera czas wstrzyknięcia skryptów między "document_end" a chwilą, gdy windowonload zdarzenie zostanie uruchomione. Dokładny moment wstrzyknięcia zależy od złożoności dokumentu i czasu jego wczytywania oraz jest optymalizowany pod kątem szybkości wczytywania strony.

Skrypty treści uruchamiane w momencie "document_idle" nie muszą nasłuchiwać zdarzenia window.onload, ponieważ zawsze są wykonywane po zakończeniu wczytywania DOM. Jeśli skrypt musi być uruchamiany po window.onload, rozszerzenie może sprawdzić, czy onload zostało już wywołane, korzystając z właściwości document.readyState.
document_start {: #document_start } ciąg znaków Skrypty są wstrzykiwane po dowolnych plikach z folderu css, ale przed utworzeniem dowolnego innego DOM-u lub uruchomieniem dowolnego innego skryptu.
document_end {: #document_end } ciąg znaków Skrypty są wstrzykiwane natychmiast po utworzeniu modelu DOM, ale przed załadowaniem zasobów podrzędnych, takich jak obrazy i ramki.

Określanie ramek

Pole "all_frames" umożliwia rozszerzeniu określenie, czy pliki JavaScript i CSS mają być wstrzykiwane do wszystkich ramek odpowiadających określonym wymaganiom adresu URL, czy tylko do najwyższej ramki w 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 ustawiona wartość to false, co oznacza, że dopasowywana jest tylko najwyższa klatka.

Jeśli ustawisz wartość true, wstrzyknięcie zostanie wstawione do wszystkich klatek, nawet jeśli nie jest to najwyższa klatka na karcie. Każda ramka jest sprawdzana niezależnie pod kątem wymagań dotyczących adresu URL. Jeśli nie są one spełnione, nie zostanie wstrzyknięta do ramek podrzędnych.

Komunikacja ze stroną, na której znajduje się element docelowy

Chociaż środowiska wykonywania skryptów treści i stron, na których są one hostowane, są od siebie odizolowane, mają wspólny dostęp do modelu DOM strony. Jeśli strona chce komunikować się ze skryptem treści lub z rozszerzeniem za pomocą skryptu treści, musi to robić za pomocą udostępnionego DOM.

Przykładowe użycie 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, publikuje wiadomości do siebie. Skrypt treści przechwytuje i sprawdza tę wiadomość, a następnie przekazuje ją do procesu rozszerzenia. W ten sposób strona umożliwia komunikację w ramach procesu rozszerzania. Odwrotne przekształcenie jest możliwe za pomocą podobnych metod.

Zadbaj o bezpieczeństwo

Chociaż izolowane światy zapewniają pewien poziom ochrony, skrypty dotyczące zawartości mogą tworzyć 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 zadbać o odfiltrowanie ataków skryptowych między witrynami. Komunikacja tylko przez HTTPS, aby uniknąć ataków "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 wybieraj bezpieczniejsze interfejsy 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);