Au cœur du navigateur Web moderne (partie 2)

Mariko Kosaka

Que se passe-t-il pendant la navigation ?

Il s'agit du deuxième article d'une série de quatre articles de blog sur le fonctionnement interne de Chrome. Dans le post précédent, nous avons vu comment les différents processus et threads gèrent différentes parties d'un navigateur. Dans cet article, nous expliquons plus en détail comment chaque processus et chaque thread communiquent pour afficher un site web.

Examinons un cas d'utilisation simple de la navigation sur le Web: vous saisissez une URL dans un navigateur, puis celui-ci récupère des données sur Internet et affiche une page. Dans cet article, nous allons nous concentrer sur l'étape où un utilisateur demande un site et que le navigateur se prépare à afficher une page. C'est ce qu'on appelle la navigation.

Cela commence par un processus de navigateur

Processus du navigateur
Figure 1: UI du navigateur en haut ; schéma du processus du navigateur avec UI, réseau et thread de stockage en bas

Comme nous l'avons vu dans la partie 1: CPU, GPU, mémoire et architecture multiprocessus, tout ce qui se trouve en dehors d'un onglet est géré par le processus du navigateur. Le processus du navigateur comporte des threads, tels que le thread UI qui dessine les boutons et les champs de saisie du navigateur, le thread réseau qui gère la pile réseau pour recevoir les données provenant d'Internet, le thread de stockage qui contrôle l'accès aux fichiers, etc. Lorsque vous saisissez une URL dans la barre d'adresse, votre saisie est gérée par le thread UI du processus du navigateur.

Une navigation simple

Étape 1: Traiter les entrées

Lorsqu'un utilisateur commence à saisir du texte dans la barre d'adresse, la première chose que le thread UI demande est "S'agit-il d'une requête de recherche ou d'une URL ?". Dans Chrome, la barre d'adresse est également un champ de saisie de recherche. Le thread UI doit donc analyser et décider de vous renvoyer vers un moteur de recherche ou vers le site demandé.

Gérer les entrées utilisateur
Figure 1: Thread UI demandant si l'entrée est une requête de recherche ou une URL

Étape 2: Démarrez la navigation

Lorsqu'un utilisateur appuie sur la touche Entrée, le thread UI lance un appel réseau pour obtenir le contenu du site. L'icône de chargement s'affiche dans l'angle d'un onglet et le thread réseau passe par des protocoles appropriés tels que la recherche DNS et l'établissement d'une connexion TLS pour la requête.

Démarrage de la navigation
Figure 2: Thread UI communiquant avec le thread réseau pour accéder à monsite.com

À ce stade, le thread réseau peut recevoir un en-tête de redirection du serveur tel que HTTP 301. Dans ce cas, le thread réseau communique avec le thread UI que le serveur demande une redirection. Une autre requête d'URL est ensuite lancée.

Étape 3: Lisez la réponse

Réponse HTTP
Figure 3: En-tête de réponse contenant Content-Type et la charge utile correspondant aux données réelles

Une fois que le corps de la réponse (charge utile) commence à arriver, le thread réseau examine les premiers octets du flux, si nécessaire. L'en-tête Content-Type de la réponse doit indiquer le type de données dont il s'agit, mais comme il peut être manquant ou incorrect, le reniflage du type MIME est effectué ici. Comme indiqué dans le code source, cette activité est complexe. Vous pouvez lire le commentaire pour voir comment les différents navigateurs traitent les paires contenu-type/charge utile.

Si la réponse est un fichier HTML, l'étape suivante consiste à transmettre les données au processus de moteur de rendu. Toutefois, s'il s'agit d'un fichier ZIP ou d'un autre fichier, il s'agit d'une requête de téléchargement. Les données doivent donc être transmises au gestionnaire de téléchargement.

Reniflage du type MIME
Figure 4: Thread réseau demandant si les données de réponse sont au format HTML provenant d'un site sûr

C'est également à ce niveau que s'effectue la vérification de la SafeBrowsing. Si le domaine et les données de réponse semblent correspondre à un site malveillant connu, le thread réseau envoie une alerte pour afficher une page d'avertissement. De plus, une vérification Cross Origin Read Blocking (CORB) est effectuée pour s'assurer que les données intersites sensibles n'atteignent pas le processus du moteur de rendu.

Étape 4: Rechercher un processus de moteur de rendu

Une fois que toutes les vérifications sont effectuées et que le thread réseau est sûr que le navigateur doit accéder au site demandé, le thread réseau indique au thread UI que les données sont prêtes. Le thread UI trouve ensuite un processus de rendu pour poursuivre l'affichage de la page Web.

Trouver un processus de moteur de rendu
Figure 5: Thread réseau indiquant au thread UI de trouver le processus du moteur de rendu

Étant donné que la requête réseau peut prendre plusieurs centaines de millisecondes pour obtenir une réponse, une optimisation permettant d'accélérer ce processus est appliquée. Lorsque le thread UI envoie une requête URL au thread réseau à l'étape 2, il sait déjà vers quel site il accède. Le thread UI tente de trouver ou de démarrer de manière proactive un processus de moteur de rendu parallèlement à la requête réseau. De cette façon, si tout se passe comme prévu, un processus de moteur de rendu est déjà en position de veille lorsque le thread réseau a reçu des données. Ce processus de secours peut ne pas être utilisé si la navigation redirige les utilisateurs d'un site à un autre. Dans ce cas, un processus différent peut être nécessaire.

Étape 5: Valider la navigation

Maintenant que les données et le processus de moteur de rendu sont prêts, un IPC est envoyé du processus de navigateur au processus de rendu pour valider la navigation. Il transmet également le flux de données afin que le processus de rendu puisse continuer à recevoir des données HTML. Une fois que le processus du navigateur entend la confirmation que la validation s'est produite dans le processus du moteur de rendu, la navigation est terminée et la phase de chargement du document commence.

À ce stade, la barre d'adresse est mise à jour, et l'indicateur de sécurité et l'interface utilisateur des paramètres du site reflètent les informations de la nouvelle page. L'historique des sessions de l'onglet sera mis à jour de sorte que les boutons "Précédent/Suivant" parcourent le site auquel je viens d'accéder. Pour faciliter la restauration d'onglets ou de sessions lorsque vous fermez un onglet ou une fenêtre, l'historique des sessions est stocké sur le disque.

Valider la navigation
Figure 6: IPC entre les processus du navigateur et du moteur de rendu, demandant l'affichage de la page

Étape supplémentaire: Chargement initial terminé

Une fois la navigation validée, le processus de rendu continue à charger les ressources et affiche la page. Nous verrons en détail ce qui se passe à ce stade dans le prochain message. Une fois que le processus de rendu a "terminé" le rendu, il renvoie un IPC au processus du navigateur (c'est-à-dire une fois que tous les événements onload ont été déclenchés sur tous les frames de la page et que leur exécution est terminée). À ce stade, le thread UI arrête l'icône de chargement dans l'onglet.

Je dis "terminé", car le JavaScript côté client pouvait toujours charger des ressources supplémentaires et afficher de nouvelles vues après ce point.

Chargement de la page terminé
Figure 7: IPC entre le moteur de rendu et le processus du navigateur pour signaler que la page est "chargée"

La navigation simple était terminée ! Mais que se passe-t-il si un utilisateur ajoute à nouveau une URL différente pour la barre d'adresse ? Le processus du navigateur passe par les mêmes étapes pour accéder à un autre site. Mais avant de pouvoir le faire, il doit vérifier si le site actuellement affiché s'intéresse à l'événement beforeunload.

beforeunload peut créer une alerte "Quitter ce site ?" lorsque vous essayez de quitter l'onglet ou de le fermer. Tout ce qui se trouve dans un onglet, y compris votre code JavaScript, est géré par le processus de rendu. Le processus du navigateur doit donc vérifier le processus actuel du moteur de rendu lorsqu'une nouvelle requête de navigation arrive.

Gestionnaire d'événements beforeunload
Figure 8: IPC entre le processus du navigateur et un processus de moteur de rendu indiquant qu'il est sur le point d'accéder à un autre site

Si la navigation a été lancée à partir du processus du moteur de rendu (par exemple, si un utilisateur a cliqué sur un lien ou si le code JavaScript côté client a exécuté window.location = "https://newsite.com"), le processus du moteur de rendu vérifie d'abord les gestionnaires beforeunload. Ensuite, il suit le même processus que la navigation déclenchée par le processus du navigateur. La seule différence est que la requête de navigation est lancée du processus de rendu vers le processus du navigateur.

Lorsque la nouvelle navigation est effectuée sur un site différent de celui actuellement affiché, un processus de rendu distinct est appelé pour gérer la nouvelle navigation, tandis que le processus de rendu actuel est conservé pour gérer des événements tels que unload. Pour en savoir plus, consultez Présentation des états du cycle de vie de la page et découvrez comment associer des événements à l'aide de l'API Page Lifecycle.

nouvelle navigation et décharger
Figure 9: Deux IPC d'un processus de navigateur à un nouveau processus de moteur de rendu indiquant d'afficher la page et de décharger l'ancien processus de moteur de rendu

Dans le cas d'un service worker

Une modification récente de ce processus de navigation est l'introduction du service worker. Service worker est un moyen d'écrire un proxy réseau dans le code de votre application. Il permet aux développeurs Web de mieux contrôler les éléments à mettre en cache localement et à quel moment récupérer les nouvelles données du réseau. Si le service worker est configuré pour charger la page à partir du cache, il n'est pas nécessaire de demander les données au réseau.

Il est important de garder à l'esprit que le service worker correspond à un code JavaScript qui s'exécute dans un processus de moteur de rendu. Mais lorsque la requête de navigation arrive, comment un processus de navigateur sait-il que le site a un service worker ?

Recherche de niveau d'accès de service worker
Figure 10: Thread réseau dans le processus du navigateur recherchant le champ d'application du service worker

Lorsqu'un service worker est enregistré, le champ d'application de ce service est conservé à titre de référence. Pour en savoir plus à ce sujet, consultez l'article Cycle de vie d'un service worker. Lorsqu'une navigation a lieu, le thread réseau compare le domaine aux niveaux d'accès du service worker enregistrés. Si un service worker est enregistré pour cette URL, le thread UI trouve un processus de moteur de rendu afin d'exécuter le code du service worker. Le service worker peut charger des données à partir du cache, éliminant le besoin de demander des données au réseau, ou bien demander de nouvelles ressources à partir du réseau.

navigation service worker
Figure 11: Thread UI d'un processus de navigateur démarrant un processus de moteur de rendu pour gérer les service workers ; un thread de nœud de calcul dans un processus de moteur de rendu demande ensuite des données au réseau

Comme vous pouvez le constater, cet aller-retour entre le processus du navigateur et le processus du moteur de rendu peut entraîner des retards si le service worker décide finalement de demander des données au réseau. Le préchargement de navigation est un mécanisme permettant d'accélérer ce processus en chargeant des ressources parallèlement au démarrage du service worker. Il marque ces requêtes avec un en-tête, ce qui permet aux serveurs de décider d'envoyer un contenu différent pour ces requêtes ; par exemple, simplement des données mises à jour au lieu d'un document complet.

Préchargement de la navigation
Figure 12: Thread UI d'un processus de navigateur démarrant un processus de moteur de rendu pour gérer le service worker lors du lancement d'une requête réseau en parallèle

Conclusion

Dans cet article, nous avons examiné ce qui se passe pendant une navigation et comment le code de votre application Web, tel que les en-têtes de réponse et le code JavaScript côté client, interagissent avec le navigateur. Connaître les étapes que le navigateur suit pour obtenir des données du réseau permet de comprendre plus facilement pourquoi des API telles que le préchargement de navigation ont été développées. Dans le prochain article, nous verrons comment le navigateur évalue le code HTML/CSS/JavaScript pour afficher les pages.

Avez-vous apprécié ce post ? Si vous avez des questions ou des suggestions pour un prochain post, n'hésitez pas à me contacter dans la section des commentaires ci-dessous ou sur @kosamari sur Twitter.

Étape suivante: Fonctionnement interne d'un processus de moteur de rendu