Przekazywanie wiadomości

Ponieważ skrypty treści działają w kontekście strony internetowej, a nie w rozszerzeniu, często wymagają w celu komunikacji z pozostałymi elementami rozszerzenia. Na przykład rozszerzenie czytnika RSS może użyć funkcji skrypty treści, aby wykryć obecność kanału RSS na stronie, a następnie powiadomić stronę działającą w tle aby została wyświetlona ikona czynności na stronie.

Komunikacja między rozszerzeniami a ich skryptami treści opiera się na przekazywaniu wiadomości. Oba modele może nasłuchiwać wiadomości wysyłanych z drugiej strony i odpowiadać na tym samym kanale. Wiadomość może zawierać dowolny prawidłowy obiekt JSON (null, wartość logiczna, liczba, ciąg, tablica lub obiekt). Jest prosta Interfejs API dla żądań jednorazowych i bardziej złożony API, który zapewnia długi okres eksploatacji połączeń w celu wymiany wielu komunikatów we wspólnym kontekście. Możesz też wysłać wiadomości do innego rozszerzenia, jeśli znasz jego identyfikator, który jest uwzględniony w rozszerzeniu wielu wiadomości.

Proste żądania jednorazowe

Jeśli chcesz wysłać tylko jedną wiadomość do innej części rozszerzenia (oraz opcjonalnie uzyskać ), użyj uproszczonej metody runtime.sendMessage lub tabs.sendMessage . Dzięki temu możesz wysłać jednorazową wiadomość podlegającą serializacji w formacie JSON ze skryptu treści do rozszerzenia lub odwrotnie odpowiednio . Opcjonalny parametr wywołania zwrotnego umożliwia obsługę odpowiedzi stronie, o ile istnieje.

Wysyłanie żądania ze skryptu treści wygląda tak:

chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
  console.log(response.farewell);
});

Wysyłanie żądania z rozszerzenia do skryptu treści wygląda podobnie, z tą różnicą, że i określ, na którą kartę ma być wysłany. Ten przykład pokazuje wysłanie wiadomości do skryptu treści na wybranej karcie.

chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
  chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
    console.log(response.farewell);
  });
});

Po stronie odbiorcy musisz skonfigurować detektor zdarzeń runtime.onMessage do obsługi . Wygląda to tak samo ze skryptu treści lub strony rozszerzenia.

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    console.log(sender.tab ?
                "from a content script:" + sender.tab.url :
                "from the extension");
    if (request.greeting == "hello")
      sendResponse({farewell: "goodbye"});
  }
);

W powyższym przykładzie funkcja sendResponse była wywoływana synchronicznie. Jeśli chcesz asynchronicznie używać funkcji sendResponse, dodaj return true; do modułu obsługi zdarzeń onMessage.

Uwaga: jeśli wiele stron nasłuchuje zdarzeń onMessage, tylko pierwsza osoba, która wywoła funkcję sendResponse() dla danego zdarzenia, spowoduje wysłanie odpowiedzi. Pozostałe odpowiedzi na to zdarzenie zostaną zignorowane.
Uwaga: wywołanie zwrotne sendResponse jest prawidłowe tylko wtedy, gdy jest używane synchronicznie lub gdy moduł obsługi zdarzeń zwraca true, co oznacza, że zareaguje asynchronicznie. Wywołanie zwrotne funkcji sendMessage będzie wywoływane automatycznie, jeśli żadne moduły obsługi nie zwrócą wartości „prawda” lub gdy wywołanie zwrotne sendResponse zakończy się czyszczeniem pamięci.

Długotrwałe kontakty

Czasami warto prowadzić rozmowę, która trwa dłużej niż jedna prośba i odpowiedź. W takim przypadku możesz ze scenariusza treści otworzyć stronę z rozszerzeniem lub kanał, który istnieje od dłuższego czasu. na odwrót, używając odpowiednio runtime.connect lub tabs.connect . Kanał może opcjonalnie mieć nazwę, która pozwala rozróżnić różne typy połączeń.

Jednym z takich zastosowań może być automatyczne wypełnianie formularza. Skrypt ten może sprawić, że kanał stronę rozszerzenia dla konkretnego loginu i wysyłanie wiadomości do rozszerzenia dla każdego danych wejściowych na stronie, by poprosić o dane do wypełnienia. Połączenie współdzielone umożliwia rozszerzenie. aby zachować stan „udostępniany” i połączyć kilka wiadomości pochodzących ze skryptu treści.

Podczas nawiązywania połączenia każdy z końców otrzymuje obiekt runtime.Port, który jest używany do wysyłania i odbierania wiadomości za pomocą tego połączenia.

Aby otworzyć kanał przy użyciu skryptu oraz jak wysyłać i odsłuchiwać wiadomości:

var port = chrome.runtime.connect({name: "knockknock"});
port.postMessage({joke: "Knock knock"});
port.onMessage.addListener(function(msg) {
  if (msg.question == "Who's there?")
    port.postMessage({answer: "Madame"});
  else if (msg.question == "Madame who?")
    port.postMessage({answer: "Madame... Bovary"});
});

Wysyłanie żądania z rozszerzenia do skryptu treści wygląda podobnie, z tą różnicą, że i określ kartę, z którą chcesz się połączyć. Po prostu zamień połączenie na połączenie z powyższego przykładu: tabs.connect.

Aby obsługiwać połączenia przychodzące, musisz skonfigurować zdarzenie runtime.onConnect. słuchacz. Wygląda to tak samo ze skryptu treści lub strony rozszerzenia. Gdy inna część wywołuje „connect()”, to zdarzenie jest wywoływane wraz z obiektem runtime.Port, służy do wysyłania i odbierania wiadomości przez połączenie. Odpowiedź na to pytanie wygląda tak połączenia przychodzące:

chrome.runtime.onConnect.addListener(function(port) {
  console.assert(port.name == "knockknock");
  port.onMessage.addListener(function(msg) {
    if (msg.joke == "Knock knock")
      port.postMessage({question: "Who's there?"});
    else if (msg.answer == "Madame")
      port.postMessage({question: "Madame who?"});
    else if (msg.answer == "Madame... Bovary")
      port.postMessage({question: "I don't get it."});
  });
});

Czas eksploatacji portu

Porty są zaprojektowane jako dwukierunkowa metoda komunikacji między różnymi częściami rozszerzenia, gdzie: klatka (najwyższego poziomu) jest postrzegana jako najmniejsza część. Po wywołaniu tabs.connect, runtime.connect lub runtime.connectNative zostaje dodany port po utworzeniu konta. Tego portu można natychmiast używać do wysyłania wiadomości do drugiego końca przez postMessage.

Jeśli karta zawiera wiele ramek, wywołanie metody tabs.connect powoduje wiele wywołań zdarzenie runtime.onConnect (raz na każdą klatkę na karcie). Podobnie, jeśli runtime.connect, zdarzenie onConnect może być wywoływane kilka razy (raz na każdy w ramach procesu rozszerzenia).

Warto wiedzieć, kiedy połączenie zostaje zamknięte, na przykład czy utrzymujesz osobny dla każdego otwartego portu. W tym celu możesz nasłuchiwać zdarzenia runtime.Port.onDisconnect. Ten jest wywoływane, gdy po drugiej stronie kanału nie ma prawidłowych portów. Dzieje się to w w następujących sytuacjach:

  • Po drugiej stronie nie ma detektorów runtime.onConnect.
  • Karta zawierająca port zostanie wyładowana z pamięci (np. po otwarciu karty).
  • Klatka z miejsca, z którego wywołano connect, została wyładowana z pamięci.
  • Wszystkie ramki, które otrzymały port (przez runtime.onConnect), zostały wczytane.
  • Druga strona wywołuje metodę runtime.Port.disconnect. Pamiętaj, że jeśli zostanie przeprowadzone połączenie connect w wielu portach po stronie odbiorcy i disconnect() jest wywoływane z każdego z tych portów, a następnie zdarzenie onDisconnect jest wywoływane tylko na porcie nadawcy, a nie na pozostałych.

Wiadomości o różnych rozszerzeniach

Oprócz wysyłania wiadomości między różnymi komponentami rozszerzenia możesz też używać funkcji do komunikowania się z innymi rozszerzeniami. Pozwala to udostępnić publiczny interfejs API, który jest z których mogą korzystać rozszerzenia.

Nasłuchiwanie przychodzących żądań i połączeń przebiega podobnie jak w przypadku wewnętrznego – z tą różnicą, że korzystasz z runtime.onMessageExternal lub runtime.onConnectExternal. Oto przykład: każdy:

// For simple requests:
chrome.runtime.onMessageExternal.addListener(
  function(request, sender, sendResponse) {
    if (sender.id == blocklistedExtension)
      return;  // don't allow this extension access
    else if (request.getTargetData)
      sendResponse({targetData: targetData});
    else if (request.activateLasers) {
      var success = activateLasers();
      sendResponse({activateLasers: success});
    }
  });

// For long-lived connections:
chrome.runtime.onConnectExternal.addListener(function(port) {
  port.onMessage.addListener(function(msg) {
    // See other examples for sample onMessage handlers.
  });
});

Podobnie wysyłanie wiadomości do innego rozszerzenia jest podobne do wysłania wiadomości z poziomu rozszerzenia. Jedyną różnicą jest to, że musisz przekazać identyfikator rozszerzenia, z którym chcesz się komunikować. Przykład:

// The ID of the extension we want to talk to.
var laserExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";

// Make a simple request:
chrome.runtime.sendMessage(laserExtensionId, {getTargetData: true},
  function(response) {
    if (targetInRange(response.targetData))
      chrome.runtime.sendMessage(laserExtensionId, {activateLasers: true});
  }
);

// Start a long-running conversation:
var port = chrome.runtime.connect(laserExtensionId);
port.postMessage(...);

Wysyłanie wiadomości ze stron internetowych

Podobnie jak wiadomości z różnych rozszerzeń aplikacja lub rozszerzenie może otrzymywać wiadomości i odpowiadać na nie wiadomości ze zwykłych stron internetowych. Aby korzystać z tej funkcji, musisz najpierw określić to w pliku manifest.json z którymi witrynami chcesz się komunikować. Na przykład:

"externally_connectable": {
  "matches": ["*://*.example.com/*"]
}

Spowoduje to udostępnienie interfejsu API do przesyłania wiadomości na każdej stronie pasującej do określonych wzorców adresów URL. Adres URL wzorzec musi zawierać co najmniej domenę drugiego poziomu, czyli wzorce nazw hosta, takie jak „*”, „*.com”, „*.co.uk” i „*.appspot.com” są zabronione. Na stronie internetowej użyj komponentu interfejsów API runtime.sendMessage lub runtime.connect, aby wysyłać wiadomość do określonej aplikacji lub . Na przykład:

// The ID of the extension we want to talk to.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";

// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
  function(response) {
    if (!response.success)
      handleError(url);
  });

W aplikacji lub rozszerzeniu możesz odsłuchiwać wiadomości ze stron internetowych przez Interfejsy API runtime.onMessageExternal lub runtime.onConnectExternal podobne do cross-extension . Tylko strona internetowa może zainicjować połączenie. Oto przykład:

chrome.runtime.onMessageExternal.addListener(
  function(request, sender, sendResponse) {
    if (sender.url == blocklistedWebsite)
      return;  // don't allow this web page access
    if (request.openUrlInEditor)
      openUrl(request.openUrlInEditor);
  });

Komunikaty natywne

Rozszerzenia i aplikacje mogą wymieniać wiadomości z natywnymi aplikacjami zarejestrowanymi jako host natywnego przesyłania komunikatów. Więcej informacji o tej funkcji znajdziesz w artykule Komunikaty natywne.

Bezpieczeństwo

Skrypty treści są mniej wiarygodne

Skrypty treści są mniej godne zaufania niż strona działająca w tle rozszerzenia (np. szkodliwa strona może naruszyć proces renderowania, w którym uruchamiane są skrypty treści). Załóżmy, że wiadomości ze skryptu treści mogły zostać utworzone przez osobę przeprowadzającą atak i upewnij się, że weryfikacja oczyścić wszystkie dane wejściowe. Załóż, że wszelkie dane wysyłane do skryptu treści mogą wyciec na stronę internetową. Ogranicz zakres działań z podwyższonymi uprawnieniami, które mogą być wywoływane przez wiadomości odebrane z treści skryptów.

Cross-site scripting

Gdy otrzymujesz wiadomość ze skryptu treści lub innego rozszerzenia, zachowaj ostrożność podczas korzystania ze skryptów nie padły ofiarą ataków typu cross-site scripting. Ta wskazówka dotyczy skryptów działających w strony rozszerzenia w tle, a także skryptów treści działających w innych źródłach internetowych. W szczególności należy unikać używania niebezpiecznych interfejsów API, takich jak te:

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // WARNING! Might be evaluating an evil script!
  var resp = eval("(" + response.farewell + ")");
});
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // WARNING! Might be injecting a malicious script!
  document.getElementById("resp").innerHTML = response.farewell;
});

Zamiast tego korzystaj z bezpieczniejszych interfejsów API, które nie uruchamiają skryptów:

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // JSON.parse does not evaluate the attacker's scripts.
  var resp = JSON.parse(response.farewell);
});
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // innerText does not let the attacker inject HTML elements.
  document.getElementById("resp").innerText = response.farewell;
});

Przykłady

Proste przykłady komunikowania się za pomocą wiadomości znajdziesz w sekcji examples/api/messaging. katalogu. Przykład natywnego przesyłania komunikatów pokazuje, jak aplikacja Chrome może komunikować się z aplikacji natywnej. Więcej przykładów i pomoc w wyświetlaniu kodu źródłowego znajdziesz w przykładach.