Nowoczesna przeglądarka internetowa (część 2)

Mariko Kosaka

Co dzieje się podczas nawigacji

To jest część 2 z 4-częściowej serii na blogu o tym, jak działa Chrome. W poprzednim poście omówiliśmy, jak różne procesy i wątki obsługują różne części przeglądarki. W tym poście szczegółowo omawiamy komunikację między procesami i wątkami, aby umożliwić wyświetlanie strony internetowej.

Przyjrzyjmy się prostemu przypadkowi przeglądania internetu: wpisujesz adres URL w przeglądarce, a potem przeglądarka pobiera dane z internetu i wyświetla stronę. W tym poście skupimy się na etapie, na którym użytkownik wysyła żądanie udostępnienia witryny, a przeglądarka przygotowuje się do jej wyrenderowania. Jest to tzw. nawigacja.

Zaczyna się od procesu przeglądarki

Procesy przeglądarki
Rysunek 1. Interfejs przeglądarki u góry; schemat procesu przeglądarki z wątkiem UI, sieci i pamięci masowej w dolnej części

Jak omówiliśmy w części 1. Procesor, GPU, pamięć i architektura wieloprocesowa, wszystko poza kartą jest obsługiwane przez proces przeglądarki. W procesie przeglądarki są takie wątki jak wątek UI, który rysuje przyciski i pola do wprowadzania danych w przeglądarce, wątek sieciowy, który zajmuje się stosem sieciowym, aby odbierać dane z internetu, wątek pamięci, który kontroluje dostęp do plików, i nie tylko. Gdy wpisujesz adres URL na pasku adresu, dane wejściowe są obsługiwane przez wątek UI procesu przeglądarki.

Prosta nawigacja

Krok 1. Obsługa danych wejściowych

Gdy użytkownik zaczyna pisać w pasku adresu, pierwszy wątek interfejsu użytkownika pyta „Czy to jest wyszukiwane hasło, czy URL?”. W Chrome pasek adresu jest również polem do wprowadzania danych wyszukiwania, więc wątek UI musi zostać przeanalizowany i zdecydować, czy przekierować Cię do wyszukiwarki, czy do żądanej witryny.

Obsługa danych wejściowych użytkownika
Rysunek 1. Wątek interfejsu z pytaniem, czy dane wejściowe są zapytaniem czy adresem URL

Krok 2. Rozpocznij nawigację

Gdy użytkownik naciśnie klawisz Enter, wątek UI inicjuje wywołanie sieciowe w celu pobrania treści witryny. W rogu karty wyświetla się ikona wczytywania, a wątek sieci przechodzi przez odpowiednie protokoły, takie jak wyszukiwanie DNS i nawiązuje połączenie TLS dla żądania.

Rozpoczęcie nawigacji
Rysunek 2. Wątek interfejsu komunikujący się z wątkiem sieci w celu przejścia do witryny mojawitryna.com

W tym momencie wątek sieciowy może otrzymać nagłówek przekierowania serwera, taki jak HTTP 301. W takim przypadku wątek sieci komunikuje się z wątkiem UI, którego serwer żąda przekierowania. Następnie jest wysyłane kolejne żądanie adresu URL.

Krok 3. Przeczytaj odpowiedź

Odpowiedź HTTP
Rysunek 3. Nagłówek odpowiedzi zawierający element Content-Type i ładunek będący rzeczywistymi danymi

Gdy zacznie przychodzić treść odpowiedzi (ładunek), w razie potrzeby wątek sieciowy sprawdza kilka pierwszych bajtów strumienia. Nagłówek Content-Type odpowiedzi powinien zawierać informacje o typie danych, ale ponieważ może ich brakować lub być nieprawidłowe, w tym miejscu odbywa się sniffing typu MIME. Jest to „podejrzana firma”, o czym wspomniano w kodzie źródłowym. Możesz przeczytać komentarz, aby zobaczyć, jak różne przeglądarki traktują pary typ treści i ładunek.

Jeśli odpowiedź jest plikiem HTML, kolejnym krokiem jest przekazanie danych do mechanizmu renderowania. Jeśli jest to plik ZIP lub inny plik, oznacza to, że jest to żądanie pobrania, więc dane muszą przekazać do menedżera pobierania.

Wychwytywanie typów MIME
Rysunek 4. Wątek sieciowy z pytaniem, czy dane odpowiedzi to HTML z bezpiecznej witryny

Tutaj odbywa się też kontrola SafeBrowsing. Jeśli domena i dane odpowiedzi wydają się być zgodne ze znaną szkodliwą witryną, alert o wątku sieciowym wyświetli stronę z ostrzeżeniem. Dodatkowo przeprowadzamy CCCblokowania (C), aby zapewnić, że poufne dane z innych witryn nie trafią do procesu renderowania.

Krok 4. Znajdź proces renderowania

Po zakończeniu weryfikacji, a wątek sieci będzie mieć pewność, że przeglądarka powinna przejść do żądanej witryny, a wątek sieci informuje wątek UI, że dane są gotowe. Wątek UI znajduje następnie proces renderowania, który kontynuuje renderowanie strony.

Znajdź proces renderowania
Rysunek 5. Wątek sieci informujący wątek interfejsu, aby znaleźć proces renderowania

Uzyskanie odpowiedzi na żądanie sieciowe może trwać kilkaset milisekund, dlatego zastosowaliśmy optymalizację w celu przyspieszenia tego procesu. Gdy w kroku 2 wątek UI wysyła do wątku sieci żądanie adresu URL, wie już, do której witryny prowadzi. Wątek UI próbuje aktywnie znaleźć lub uruchomić proces renderowania równolegle do żądania sieciowego. Dzięki temu, jeśli wszystko pójdzie zgodnie z oczekiwaniami, mechanizm renderowania będzie już w pozycji gotowości, gdy wątek sieciowy odebrał dane. Ten proces gotowości może nie być używany, jeśli nawigacja między witrynami jest przekierowywana. W takim przypadku konieczne może być zastosowanie innego procesu.

Krok 5. Zatwierdź nawigację

Dane i proces renderowania są już gotowe, więc z procesu przeglądarki do mechanizmu renderowania jest wysyłany IPC w celu zatwierdzenia nawigacji. Strumień danych przekazuje też strumień danych, dzięki czemu proces renderowania może dalej odbierać dane HTML. Gdy proces przeglądarki usłyszy potwierdzenie, że zatwierdzenie miało miejsce w procesie renderowania, nawigacja dobiega końca i rozpoczyna się faza wczytywania dokumentu.

Pasek adresu jest teraz zaktualizowany, a wskaźnik bezpieczeństwa i interfejs ustawień witryny odzwierciedlają informacje o nowej stronie. Historia sesji na tej karcie zostanie zaktualizowana, więc przyciski Wstecz/Dalej będą przechodzić przez odwiedzaną właśnie witrynę. Aby ułatwić przywracanie kart/sesji po zamknięciu karty lub okna, historia sesji jest przechowywana na dysku.

Zatwierdź nawigację
Rys. 6. Protokół IPC między przeglądarką a procesem renderowania – żądanie wyrenderowania strony

Dodatkowy krok: wstępne wczytywanie zakończone

Po zatwierdzeniu nawigacji proces renderowania kontynuuje wczytywanie zasobów i renderuje stronę. Cały proces zostanie szczegółowo omówiony w następnym poście. Gdy mechanizm renderowania „zakończy” renderowanie, wysyła kod IPC z powrotem do procesu przeglądarki (dzieje się to po wywołaniu wszystkich zdarzeń onload we wszystkich ramkach na stronie i zakończeniu wykonywania). W tym momencie wątek interfejsu zatrzymuje wskaźnik postępu ładowania na karcie.

Mówię „finishes”, ponieważ od tego momentu JavaScript po stronie klienta wciąż może wczytać dodatkowe zasoby i wyrenderować nowe widoki.

Zakończ wczytywanie strony
Rysunek 7. Protokół IPC z mechanizmu renderowania do procesu przeglądarki powiadamiający, że strona została wczytana

To już wszystko. Co się jednak stanie, jeśli użytkownik ponownie wpisze inny adres URL na pasku adresu? Cóż, proces przeglądarki wykonuje te same kroki, aby przejść do innej witryny. Zanim będzie to możliwe, musi sprawdzić aktualnie wyrenderowaną witrynę, czy zależy jej na zdarzeniu beforeunload.

beforeunload może utworzyć alert „Opuścić tę witrynę?”, gdy próbujesz opuścić lub zamknąć kartę. Cała zawartość karty, w tym kod JavaScript, jest obsługiwana przez mechanizm renderowania, więc gdy pojawia się nowe żądanie nawigacji, proces przeglądarki musi kontrolować bieżący mechanizm renderowania.

moduł obsługi zdarzeń beforeunload
Rysunek 8. Protokół IPC z procesu przeglądarki do procesu renderowania informującego, że nastąpi przejście do innej witryny

Jeśli nawigacja została zainicjowana przez proces renderowania (np. użytkownik kliknął link lub uruchomił się kod JavaScript po stronie klienta window.location = "https://newsite.com"), mechanizm renderowania najpierw sprawdza moduły obsługi beforeunload. Następnie przebiega tak samo jak nawigacja zainicjowana przez proces przeglądarki. Jedyna różnica polega na tym, że żądanie nawigacji jest inicjowane z procesu renderowania do procesu przeglądarki.

Gdy nowa nawigacja zostanie przekierowana do innej witryny niż aktualnie renderowana, wywołany jest oddzielny proces renderowania, który obsługuje nową nawigację, a bieżący proces renderowania jest zachowywany w celu obsługi zdarzeń takich jak unload. Aby dowiedzieć się więcej, przeczytaj omówienie stanów cyklu życia strony i dowiedz się, jak dołączać do zdarzeń za pomocą interfejsu Page Lifecycle API.

nowa nawigacja i unload
Rysunek 9. 2. Przebieg procesu przeglądarki do nowego procesu renderowania, który informuje o renderowaniu strony i wykorzystywaniu starego procesu renderowania

W przypadku skryptu service worker

Jedną z niedawnych zmian w tym procesie nawigacji jest wprowadzenie skryptu service worker. Skrypt service worker pozwala zapisać sieciowy serwer proxy w kodzie aplikacji, co daje programistom stron internetowych większą kontrolę nad tym, co ma być zapisywane lokalnie w pamięci podręcznej, a kiedy z siecią. Jeśli skrypt service worker jest ustawiony na wczytywanie strony z pamięci podręcznej, nie ma potrzeby żądania danych z sieci.

Pamiętaj, że skrypt service worker to kod JavaScript uruchamiany w procesie renderowania. Gdy jednak pojawia się żądanie nawigacji, skąd przeglądarka może wiedzieć, że witryna ma skrypt service worker?

Wyszukiwanie zakresu skryptu service worker
Rysunek 10: wątek sieci w procesie przeglądarki wyszukuje zakres skryptu service worker

Gdy skrypt service worker jest zarejestrowany, jego zakres jest zachowywany jako punkt odniesienia (więcej informacji o zakresie znajdziesz w tym artykule). Gdy następuje nawigacja, wątek sieci sprawdza domenę pod kątem zarejestrowanych zakresów skryptu service worker. Jeśli skrypt service worker jest zarejestrowany dla tego adresu URL, wątek interfejsu znajduje proces renderowania w celu wykonania kodu skryptu service worker. Skrypt service worker może wczytywać dane z pamięci podręcznej, eliminując potrzebę wysyłania żądań danych do sieci lub żądając nowych zasobów z sieci.

nawigacja w skryptach service worker
Rysunek 11. Wątek UI w procesie przeglądarki rozpoczynający proces renderowania do obsługi instancji roboczych usług; wątek instancji roboczej w procesie renderowania żąda danych z sieci

Widać, że podróż w obie strony między procesem przeglądarki a mechanizmem renderowania może spowodować opóźnienia, jeśli skrypt service worker w końcu zdecyduje się wysłać żądanie danych do sieci. Wstępne wczytywanie nawigacji to mechanizm przyspieszający ten proces przez ładowanie zasobów równolegle do uruchamiania skryptu service worker. Oznacza on te żądania nagłówkiem, dzięki czemu serwery mogą wysyłać inne treści w odpowiedzi na żądania, np. tylko zaktualizowane dane, a nie cały dokument.

Wstępne wczytywanie nawigacji
Rysunek 12. Wątek UI w procesie przeglądarki uruchamiający proces renderowania do obsługi skryptu service worker przy równoległym inicjowaniu żądania sieciowego

Podsumowanie

W tym poście omówiliśmy, co dzieje się podczas nawigacji i jak kod Twojej aplikacji internetowej, np. nagłówki odpowiedzi i JavaScript po stronie klienta, współdziałają z przeglądarką. Znajomość czynności, jakie musi wykonać przeglądarka, aby pobrać dane z sieci, ułatwia zrozumienie, dlaczego tak się stało. W następnym poście dowiemy się, jak przeglądarka ocenia kod HTML/CSS/JavaScript pod kątem renderowania stron.

Podobał Ci się post? Jeśli masz jakieś pytania lub sugestie dotyczące kolejnego posta, chętnie poznam Twoją opinię w sekcji komentarzy poniżej lub na Twitterze – @kosamari.

Dalej: wewnętrzne działanie procesu renderowania