Uno sguardo all'interno del browser web moderno (parte 2)

Mariko Kosaka

Cosa succede nella navigazione

Questa è la seconda parte di una serie di quattro blog che illustra il funzionamento interno di Chrome. Nel post precedente, abbiamo visto in che modo i diversi processi e i thread gestiscono parti diverse di un browser. In questo post, analizziamo meglio come ogni processo e ogni thread comunicano per mostrare un sito web.

Esaminiamo un caso d'uso semplice della navigazione sul web: in un browser devi digitare un URL e il browser recupera i dati da internet e mostra una pagina. In questo post ci concentreremo sulla parte in cui un utente richiede un sito e il browser si prepara a visualizzare una pagina, nota anche come navigazione.

Inizia con un processo del browser

Processi del browser
Figura 1: UI del browser in alto, diagramma del processo del browser con UI, rete e thread di archiviazione all'interno della parte inferiore

Come abbiamo visto nella parte 1: CPU, GPU, memoria e architettura multi-processo, tutto al di fuori di una scheda viene gestito dal processo del browser. Il processo del browser include thread come il thread UI che disegna pulsanti e campi di input del browser, il thread di rete che gestisce lo stack di rete per ricevere dati da internet, il thread di archiviazione che controlla l'accesso ai file e altro ancora. Quando digiti un URL nella barra degli indirizzi, l'input viene gestito dal thread dell'interfaccia utente del processo del browser.

Facilità di navigazione

Passaggio 1: gestione dell'input

Quando un utente inizia a digitare nella barra degli indirizzi, la prima cosa che il thread dell'interfaccia utente chiede è "Si tratta di una query di ricerca o di un URL?". In Chrome, la barra degli indirizzi è anche un campo di immissione di ricerca, pertanto il thread dell'interfaccia utente deve analizzare e decidere se indirizzarti a un motore di ricerca o al sito richiesto.

Gestione dell'input utente
Figura 1: thread UI che chiede se l'input è una query di ricerca o un URL

Passaggio 2: avvia la navigazione

Quando un utente preme Invio, il thread dell'interfaccia utente avvia una chiamata di rete per ottenere i contenuti del sito. La rotellina di caricamento viene visualizzata nell'angolo di una scheda e il thread di rete passa attraverso i protocolli appropriati, come la ricerca DNS e la connessione TLS per la richiesta.

Inizio navigazione
Figura 2: il thread dell'interfaccia utente che comunica con il thread di rete per accedere a mysite.com

A questo punto, il thread di rete potrebbe ricevere un'intestazione di reindirizzamento del server come HTTP 301. In questo caso, il thread di rete comunica con il thread dell'interfaccia utente che il server richiede il reindirizzamento. Verrà quindi avviata un'altra richiesta di URL.

Passaggio 3: leggi la risposta

Risposta HTTP
Figura 3: intestazione della risposta, che contiene Content-Type e il payload, ovvero i dati effettivi

Quando inizia a entrare il corpo della risposta (payload), il thread di rete esamina i primi byte del flusso, se necessario. L'intestazione Content-Type della risposta dovrebbe indicare il tipo di dati, ma poiché potrebbe mancare o non essere corretto, lo sniffing del tipo MIME viene eseguito qui. Questa è una "attività complessa" come commentato nel codice sorgente. Puoi leggere il commento per vedere in che modo i diversi browser trattano le coppie contenuto-tipo/carico utile.

Se la risposta è un file HTML, il passaggio successivo consiste nel passare i dati al processo del renderer, ma se si tratta di un file ZIP o di un altro file, significa che si tratta di una richiesta di download, quindi è necessario passare i dati al gestore dei download.

Sniffing tipo MIME
Figura 4: thread di rete che chiede se i dati di risposta sono HTML provenienti da un sito sicuro

È qui anche che viene eseguito il controllo SafeBrowsing. Se il dominio e i dati della risposta sembrano corrispondere a un sito dannoso noto, gli avvisi del thread di rete visualizzano una pagina di avviso. Inoltre, viene eseguito il controllo Costro Origin Read Blocking (CORB) per garantire che i dati sensibili tra siti non siano inviati al processo di rendering.

Passaggio 4: trova un processo di rendering

Una volta completati tutti i controlli e quando il thread di rete è sicuro che il browser debba accedere al sito richiesto, il thread di rete indica al thread dell'interfaccia utente che i dati sono pronti. Il thread dell'interfaccia utente trova quindi un processo di rendering per continuare il rendering della pagina web.

Trova processo di rendering
Figura 5: thread di rete che indica al thread dell'interfaccia utente di trovare il processo del renderer

Poiché la richiesta di rete potrebbe richiedere diverse centinaia di millisecondi per ricevere una risposta, viene applicata un'ottimizzazione per velocizzare il processo. Quando il thread dell'interfaccia utente invia una richiesta di URL al thread di rete al passaggio 2, sa già a quale sito si sta spostando. Il thread dell'interfaccia utente prova a trovare o avviare in modo proattivo un processo di rendering in parallelo alla richiesta di rete. In questo modo, se tutto procede come previsto, un processo del renderer è già in posizione di standby quando il thread di rete ha ricevuto dati. Se la navigazione reindirizza tra siti, questo processo in standby potrebbe non essere utilizzato, nel qual caso potrebbe essere necessario un processo diverso.

Passaggio 5: esegui il commit della navigazione

Ora che i dati e il processo di rendering sono pronti, un IPC viene inviato dal processo del browser al processo di renderingr per eseguire il commit della navigazione. Trasmette inoltre lo stream di dati in modo che il processo di renderer possa continuare a ricevere dati HTML. Quando il processo del browser rileva la conferma del commit nel processo di rendering, la navigazione viene completata e inizia la fase di caricamento del documento.

A questo punto, la barra degli indirizzi viene aggiornata e l'indicatore di sicurezza e l'interfaccia utente delle impostazioni del sito riflettono le informazioni sul sito della nuova pagina. La cronologia delle sessioni della scheda verrà aggiornata in modo che i pulsanti Avanti/Indietro passino attraverso il sito appena aperto. Per facilitare il ripristino di schede/sessioni alla chiusura di una scheda o di una finestra, la cronologia delle sessioni viene archiviata su disco.

Esegui il commit della navigazione
Figura 6: IPC tra i processi del browser e del renderer, richiesta di rendering della pagina

Passaggio aggiuntivo: caricamento iniziale completato

Una volta eseguito il commit della navigazione, il processo di rendering continua il caricamento delle risorse ed esegue il rendering della pagina. Analizzeremo i dettagli di ciò che accade in questa fase nel prossimo post. Quando il processo del renderer "termina" il rendering, invia un IPC al processo del browser (questo avviene dopo che tutti gli eventi onload sono stati attivati su tutti i frame della pagina e sono stati eseguiti. A questo punto, il thread dell'interfaccia utente interrompe la rotellina di caricamento nella scheda.

Io dico "finishes" perché il codice JavaScript lato client potrebbe comunque caricare risorse aggiuntive e mostrare nuove viste dopo questo punto.

Termine caricamento pagina
Figura 7: IPC dal renderer al processo del browser per notificare il caricamento della pagina

La navigazione è stata completata. Ma cosa succede se un utente inserisce di nuovo un URL diverso nella barra degli indirizzi? Bene, il processo del browser segue gli stessi passaggi per accedere al sito diverso. Prima di farlo, però, deve verificare con il sito attualmente visualizzato se è interessato all'evento beforeunload.

beforeunload può creare l'avviso "Vuoi uscire da questo sito?" quando provi a uscire dalla scheda o a chiudere la scheda. Tutto ciò che si trova all'interno di una scheda, incluso il codice JavaScript, viene gestito dal processo di rendering, pertanto il processo del browser deve verificare con il processo del renderer corrente quando riceve una nuova richiesta di navigazione.

gestore di eventi beforeunload
Figura 8: IPC dal processo del browser a un processo di rendering che indica che si sta per passare a un altro sito

Se la navigazione è stata avviata dal processo del renderer (ad esempio se l'utente ha fatto clic su un link o il codice JavaScript lato client ha eseguito window.location = "https://newsite.com"), il processo del renderer controlla innanzitutto i gestori beforeunload. Quindi, segue lo stesso processo della navigazione avviata dal processo del browser. L'unica differenza è che la richiesta di navigazione viene avviata dal processo di renderingr al processo del browser.

Quando la nuova navigazione viene effettuata su un sito diverso da quello attualmente visualizzato, viene chiamato un processo di rendering separato per gestire la nuova navigazione, mentre l'attuale processo di rendering viene mantenuto per gestire eventi come unload. Per saperne di più, consulta una panoramica degli stati del ciclo di vita delle pagine e scopri come puoi collegarti agli eventi con l'API Page Lifecycle.

nuova navigazione e unload
Figura 9: due IPC da un processo del browser a un nuovo processo del renderer che indicano di eseguire il rendering della pagina e indicano l'unload del vecchio processo del renderer

In caso di service worker

Una modifica recente a questo processo di navigazione è l'introduzione del service worker. Il service worker è un modo per scrivere proxy di rete nel codice dell'applicazione; consente agli sviluppatori web di avere un maggiore controllo su cosa memorizzare localmente nella cache e su quando ottenere nuovi dati dalla rete. Se il service worker è impostato per caricare la pagina dalla cache, non è necessario richiedere i dati alla rete.

La parte importante da ricordare è che il service worker è il codice JavaScript eseguito in un processo del renderer. Ma quando arriva la richiesta di navigazione, come fa un processo del browser a sapere che il sito ha un service worker?

Ricerca dell'ambito del service worker
Figura 10: il thread di rete nel processo del browser che cerca l'ambito del service worker

Quando un service worker è registrato, l'ambito di quest'ultimo viene conservato come riferimento (per ulteriori informazioni sull'ambito, leggi l'articolo Il ciclo di vita dei service worker). Durante una navigazione, il thread di rete confronta il dominio con gli ambiti dei service worker registrati. Se un service worker è registrato per quell'URL, il thread dell'interfaccia utente trova un processo del renderer per eseguire il codice del service worker. Il service worker può caricare i dati dalla cache, eliminando la necessità di richiedere dati dalla rete, oppure richiedere nuove risorse dalla rete.

navigazione serviceworker
Figura 11: il thread dell'interfaccia utente in un processo del browser che avvia un processo del renderer per gestire i service worker; un thread worker in un processo del renderer richiede quindi i dati alla rete

Questo round trip tra il processo del browser e quello del renderer potrebbe causare ritardi se il service worker alla fine decide di richiedere dati alla rete. Il precaricamento di navigazione è un meccanismo per accelerare questo processo caricando le risorse in parallelo all'avvio del service worker. Contrassegna queste richieste con un'intestazione, consentendo ai server di decidere di inviare contenuti diversi per queste richieste; ad esempio, dati solo aggiornati anziché un documento completo.

Precaricamento navigazione
Figura 12: il thread dell'interfaccia utente in un processo del browser che avvia un processo di rendering per gestire il service worker mentre avvia la richiesta di rete in parallelo

Conclusione

In questo post, abbiamo visto cosa succede durante una navigazione e come il codice della tua applicazione web, come le intestazioni di risposta e il codice JavaScript lato client, interagiscono con il browser. Conoscere i passaggi eseguiti dal browser per ottenere dati dalla rete consente di capire più facilmente perché sono state sviluppate API come il precaricamento di navigazione. Nel prossimo post, vedremo in che modo il browser valuta i nostri codici HTML/CSS/JavaScript per il rendering delle pagine.

Ti è piaciuto il post? Se hai domande o suggerimenti per il prossimo post, sarò felice di sentirti nella sezione dei commenti qui sotto o di @kosamari su Twitter.

Successivo: Funzionamento interno di un processo del renderer