Rozszerzenia do Chrome: interfejs API rozszerzający o obsługę natychmiastowej nawigacji

Dave Tapuska
Dave Tapuska

TL;DR: zaktualizowaliśmy interfejs Extensions API, aby obsługiwać pamięć podręczną stanu strony internetowej i wstępnie wczytywać elementy nawigacyjne. Więcej informacji znajdziesz poniżej.

W Chrome ciężko pracujemy nad przyspieszeniem nawigacji. Technologie błyskawicznej nawigacji, takie jak pamięć podręczna stanu strony internetowej (wysłana w wersji na komputery w Chrome 96) i reguły spekulacyjne (dostępne w Chrome 103) usprawniają pracę zarówno w przyszłości, jak i w przyszłości. W tym poście omówimy zmiany wprowadzone w interfejsach API rozszerzeń przeglądarki, aby dostosować je do nowych przepływów pracy.

Omówienie typów stron

Przed wprowadzeniem pamięci podręcznej stanu strony internetowej i renderowania wstępnym jedna karta miała tylko 1 aktywną stronę. To był zawsze ten, który był widoczny. Jeśli użytkownik wróci na poprzednią stronę, aktywna strona zostanie zniszczona (strona B), a poprzednia strona w historii zostanie całkowicie zrekonstruowana (strona A). Rozszerzenia nie musiały martwić się o to, w jakiej części strony cyklu życia znajdowała się strona, ponieważ miała tylko jedną kartę – stan aktywny lub widoczny.

Usunięcie aktywnej strony
Usunięcie aktywnej strony.

Dzięki pamięci podręcznej stanu strony internetowej i renderowaniu wstępnym nie ma już ścisłych relacji między kartami a stronami. Teraz każda karta zawiera wiele stron i przejścia między stanami, a nie zniszczenie i rekonstrukcję.

Na przykład strona może zacząć działać jako wstępnie wyrenderowana (niewidoczna) strona, przejście w aktywną (widoczną) stronę, gdy użytkownik kliknie link, i zapisana w pamięci podręcznej stanu strony internetowej (niewidoczna), gdy użytkownik przejdzie na inną stronę, bez jej zniszczenia. W dalszej części tego artykułu przyjrzymy się nowym właściwościom, które mają pomóc rozszerzeniom w zrozumieniu, w jakim stanie są poszczególne strony.

Typy stron
Typy stron.

Pamiętaj, że karta może zawierać serię wstępnie renderowanych stron (a nie tylko jedną), jedną aktywną (widoczną) stronę oraz serię stron z pamięci podręcznej stanu strony internetowej.

Co się zmienia dla deweloperów rozszerzeń?

FrameId == 0

W Chromium najwyższą/główną ramką nazywamy ramką najbardziej zewnętrzną.

Autorzy rozszerzeń, którzy zakładają, że frameId najbardziej zewnętrznej ramki mają wartość 0 (wcześniejsza sprawdzona metoda), mogą napotkać problemy. Karta może teraz mieć wiele zewnętrznych ramek (wstępnie renderowanych i zapisanych w pamięci podręcznej), dlatego założenie, że karta ma jedną najbardziej zewnętrzną ramkę, jest błędne. frameId == 0 nadal będzie nadal reprezentować najbardziej zewnętrzną ramkę aktywnej strony, ale najbardziej zewnętrzne ramki innych stron na tej samej karcie będą miały wartość inną niż zero. Aby rozwiązać ten problem, dodano nowe pole frameType. Przeczytaj sekcję „Jak określić, czy ramka jest najbardziej zewnętrzną?” w tym poście.

Cykl życia ramek a dokumentów

Kolejnym problemem związanym z rozszerzeniami jest cykl życia ramki. Ramka zawiera dokument (powiązany z zatwierdzonym adresem URL). Dokument może się zmienić (np. w menu), ale identyfikator frameId już nie. Trudno jest więc powiązać to, co wydarzyło się w konkretnym dokumencie, tylko z identyfikatorem frameId. Wprowadzamy pojęcie documentId, czyli unikalny identyfikator każdego dokumentu. Jeśli po przejściu do ramki otworzy się nowy dokument, identyfikator się zmieni. To pole przydaje się do określania, kiedy strony zmieniają swój stan cyklu życia (między renderowaniem wstępnym/aktywnym/buforowanym), bo pozostaje on taki sam.

Zdarzenia nawigacji internetowej

Zdarzenia w przestrzeni nazw chrome.webNavigation mogą uruchamiać się wiele razy na tej samej stronie w zależności od cyklu życia, w którym się znajdują. Przeczytaj sekcje „Jak sprawdzić cykl życia strony?” i „Jak określić, kiedy strona ma przejść?”.

Jak mogę sprawdzić cykl życia strony?

Do wielu interfejsów API rozszerzeń, w przypadku których standard frameId był wcześniej dostępny, dodaliśmy typ DocumentLifecycle. Jeśli w zdarzeniu występuje typ DocumentLifecycle (np. onCommitted), jego wartością jest stan, w którym zdarzenie zostało wygenerowane. Możesz zawsze wysyłać zapytania o informacje za pomocą metod WebNavigation getFrame() i getAllFrames(), ale zawsze preferowane jest użycie wartości ze zdarzenia. Jeśli używasz którejś z metod, pamiętaj, że stan ramki może się zmienić między momentem wygenerowania zdarzenia a rozwiązaniem zwrotu obietnic obu metod.

DocumentLifecycle zawiera te wartości:

  • "prerender”: obecnie nie przedstawiane użytkownikowi, ale przygotowuje się do ewentualnego wyświetlenia.
  • "active": aktualnie wyświetlane użytkownikowi.
  • "cached": element zapisany w pamięci podręcznej stanu strony internetowej.
  • "pending_deletion": dokument jest niszczony.

Jak ustalić, czy ramka jest najbardziej zewnętrzna?

Wcześniej rozszerzenia mogły sprawdzać, czy frameId == 0, aby określić, czy zdarzenie występuje w przypadku najbardziej zewnętrznej klatki. Ponieważ na jednej karcie znajduje się wiele stron, mamy teraz wiele najbardziej zewnętrznych ramek, dlatego definicja parametru frameId jest problematyczna. Nigdy nie będziesz otrzymywać zdarzeń dotyczących klatki w pamięci podręcznej stanu strony internetowej. W przypadku wstępnie renderowanych klatek parametr frameId dla najbardziej zewnętrznej klatki ma wartość różną od 0. Dlatego użycie frameId == 0 jako sygnału do określenia, czy jest to najbardziej zewnętrzna klatka, jest nieprawidłowe.

Aby to ułatwić, wprowadziliśmy nowy typ o nazwie FrameType, dzięki czemu określenie, czy klatka jest rzeczywiście najbardziej zewnętrzną klatką, jest teraz łatwe. FrameType ma następujące wartości:

  • "outermost_frame": zwykle zwana klatką najwyższego poziomu. Zwróć uwagę, że niektóre z nich są wielokrotnością. Jeśli np. masz wstępnie renderowane i buforowane strony, każda z nich ma najbardziej zewnętrzną ramkę, którą można nazwać najwyższą ramką.
  • "fenced_frame": zarezerwowane do użycia w przyszłości.
  • "sub_frame": zwykle element iframe.

Możemy połączyć element DocumentLifecycle z parametrem FrameType i ustalić, czy ramka jest najbardziej aktywną ramką zewnętrzną. Na przykład: js tab.documentLifecycle == “active” && frameType == “outermost_frame”

Jak rozwiązywać problemy z czasem użytkowania w przypadku ramek?

Jak wspomnieliśmy powyżej, ramka zawiera dokument i może przejść do nowego dokumentu, ale frameId się nie zmieni. Wiąże się to z problemami w przypadku otrzymania zdarzenia z frameId. Jeśli wyszukasz adres URL ramki, może on być inny niż moment wystąpienia zdarzenia. Jest to tzw. problem związany z czasem użycia.

Aby rozwiązać ten problem, wprowadziliśmy metodę documentId (i parentDocumentId). W przypadku metody webNavigation.getFrame() parametr frameId jest teraz opcjonalny, jeśli podano documentId. Pole documentId zmienia się przy każdym przejściu do ramki.

Jak określić, kiedy strona się wyświetla?

Istnieją wyraźne sygnały wskazujące na to, kiedy strona przechodzi między stanami.

Przyjrzyjmy się zdarzeniom WebNavigation.

Przy pierwszej nawigacji na stronie będą widoczne cztery zdarzenia w podanej niżej kolejności. Pamiętaj, że te 4 zdarzenia mogą wystąpić, gdy stan DocumentLifecycle ma wartość "prerender" lub "active".

onBeforeNavigate
onCommitted
onDOMContentLoaded
onCompleted

Na diagramie poniżej widać, jak documentId zmienia się na "xyz", gdy wstępnie renderowana strona staje się stroną aktywną.

Identyfikator documentId zmienia się, gdy wstępnie renderowana strona staje się stroną aktywną
Parametr documentId zmieni się, gdy wstępnie renderowana strona stanie się stroną aktywną.

Gdy strona przejdzie z pamięci podręcznej stanu strony internetowej lub z renderowania wstępnego do stanu aktywnego, pojawią się 3 kolejne zdarzenia (ale w przypadku elementu DocumentLifecyle "active").

onBeforeNavigate
onCommitted
onCompleted

Element documentId pozostanie taki sam jak w oryginalnych wydarzeniach. Ilustruje to powyżej, gdy aktywuje się documentId == xyz. Pamiętaj, że uruchamiają się te same zdarzenia nawigacji z wyjątkiem zdarzenia onDOMContentLoaded, ponieważ strona została już wczytana.

Jeśli masz jakieś pytania lub komentarze, możesz je zadać w grupie Chromium-extensions.