Comment fonctionnent les navigateurs

Dans les coulisses des navigateurs Web modernes

| Tali Garsiel | Paul Irish |

Traducteurs

| ram-0000 | djibril | zoom61 | Thibaut Cuvelier | Sébastien Germez |

Cet abécédaire complet sur le fonctionnement interne de WebKit et Gecko est le résultat de beaucoup de recherches effectuées par Tali Garsiel, une développeuse israélienne. En quelques années, elle a examiné toutes les données publiées sur les fonctionnements internes du navigateur (voir Ressources) et a passé beaucoup de temps à lire le code source du navigateur Web. Elle a écrit :

« Dans les années de la domination IE à 90 %, il n'y avait pas grand-chose d'autre à faire que de considérer le navigateur comme une « boite noire ». Mais maintenant, avec des navigateurs open source ayant plus de la moitié de part de marché, c'est le bon moment pour jeter un coup d'œil sous le capot du moteur et voir ce qu'il y a dans un navigateur Web. Eh bien, ce qu'il y a à l'intérieur, ce sont des millions de lignes de C++… »

Tali a publié ses recherches sur son site, mais nous savions qu'elles méritaient un public plus large, donc nous avons tout nettoyé et republié ici.

En tant que développeur Web, apprendre le fonctionnement interne d'un navigateur vous aide à prendre de meilleures décisions et à comprendre les justifications derrière les bonnes pratiques de développement. Bien que ce document soit assez long, nous vous recommandons de passer un peu de temps à creuser dedans, nous vous garantissons que vous en serez heureux (Paul Irish, Chrome Developer Relations).

Cet article a été traduit en coréen par la communauté. HTML5 Rocks héberge les versions en allemand, espagnol, japonais, portugais, russe et chinois simplifié.

Vous pouvez aussi regarder la conférence de Tali Garsiel à ce sujet sur Vimeo.

Cet article est la traduction de l'article How Browsers Work: Behind the scenes of modern web browsers. Après votre lecture, Commentez

- Titre : Comment fonctionnent les navigateurs

- Auteurs : Tali Garsiel, Paul Irish

- Traducteurs : ram-0000, djibril, zoom61, Thibaut Cuvelier, Sébastien Germez

- Parution : 5 août 2011

- Mise à jour : 6 février 2013

- Licence : Le contenu de cet article est rédigé par Tali Garsiel et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- 1. Introduction

- 1-1. Les navigateurs dont nous allons parler

- 1-2. La fonctionnalité principale du navigateur

- 1-3. La structure haut niveau d'un navigateur

- 2. Le moteur de rendu

- 2-1. Les moteurs de rendu

- 2-2. Le flux principal

- 2-3. Exemples des flux principaux

- 3. Analyse et construction de l'arbre DOM

- 3-1. L'analyse - généralités

- 3-1-1. Les grammaires

- 3-1-2. La combinaison Parser - Lexer

- 3-1-3. La traduction

- 3-1-4. Exemple d'analyse

- 3-1-5. Définitions formelles du vocabulaire et de la syntaxe

- 3-1-6. Les types d'analyseurs

- 3-1-7. Générer des analyseurs automatiquement

- 3-2. L'analyseur HTML

- 3-2-1. La définition de la grammaire HTML

- 3-2-2. Ce n'est pas une grammaire sans contexte

- 3-2-3. La DTD HTML

- 3-2-4. Le DOM

- 3-2-5. L'algorithme d'analyse

- 3-2-6. L'algorithme de séparation

- 3-2-7. L'algorithme de construction de l'arbre

- 3-2-8. Les actions en fin d'analyse

- 3-2-9. La tolérance aux erreurs des navigateurs

- 3-2-9-1. </br> à la place de <br>

- 3-2-9-2. Une table parasite

- 3-2-9-3. Imbrication de formulaires

- 3-2-9-4. Une hiérarchie trop profonde

- 3-2-9-5. Balises HTML ou body fermantes mal placées

- 3-3. L'analyse CSS

- 3-3-1. L'analyseur CSS de Webkit

- 3-4. L'ordre d'analyse des scripts et des feuilles de style

- 3-4-1. Les scripts

- 3-4-2. L'analyse spéculative

- 3-4-3. Les feuilles de style

- 4. La construction de l'arbre de rendu

- 4-1. La relation entre l'arbre DOM et l'arbre de rendu

- 4-2. La construction de l'arbre

- 4-3. Calcul du style

- 4-3-1. Le partage des données de style

- 4-3-2. L'arbre des règles de Firefox

- 4-3-2-1. La division par structure

- 4-3-2-2. Calculer les contextes de style en utilisant l'arbre des règles

- 4-3-3. Manipuler les règles pour une recherche plus facile

- 4-3-4. Appliquer les règles dans l'ordre correct

- 4-3-4-1. L'ordre des feuilles de style

- 4-3-4-2. La spécificité

- 4-3-4-3. Trier les règles

- 4-4. Processus graduel

- 5. La mise en page

- 5-1. Le système du bit sale

- 5-2. Mise en page globale et incrémentale

- 5-3. Mise en page synchrone ou non

- 5-4. Optimisations

- 5-5. Le processus de mise en page

- 5-6. Calcul de la largeur

- 5-7. Passage à la ligne

- 6. Le dessin

- 6-1. Global et incrémental

- 6-2. L'ordre de dessin

- 6-3. Liste d'affichage de Firefox

- 6-4. Stockage des rectangles sous Webkit

- 7. Les changements dynamiques

- 8. Les threads du moteur de rendu

- 8-1. La boucle de messages

- 9. Le modèle visuel CSS2

- 9-1. La toile (canevas)

- 9-2. Le modèle des boite s CSS

- 9-3. Le schéma de positionnement

- 9-4. Les types de boites

- 9-5. Le positionnement

- 9-5-1. Relatif

- 9-5-2. Flottant

- 9-5-3. Absolu et fixe

- 9-6. La représentation en couche

- 10. Ressources

- 10-1. Traductions

- 10-2. Remerciements Developpez

1 - Introduction

Les navigateurs Web sont probablement les logiciels les plus utilisés. Dans cet abécédaire, je vais vous expliquer comment ils fonctionnent en arrière-plan. Nous allons voir ce qui se passe à partir du moment où vous tapez « google.com » dans la barre d'adresse jusqu'à ce que vous puissiez voir la page de Google dans votre navigateur.

1-1 - Les navigateurs dont nous allons parler

De nos jours, il y a cinq navigateurs principaux utilisés - Internet Explorer, Firefox, Safari, Chrome et Opera. Je vais donner des exemples à partir des navigateurs open source - Firefox, Chrome et Safari (qui est partiellement open source). Selon les statistiques de StatCounter browser statistics, actuellement (août 2011), la part d'utilisation de Firefox, Safari et Chrome ensemble est de près de 60 % (NdT pour janvier 2013, cette proportion passe à plus de 66 %). Ainsi, de nos jours, les navigateurs open source sont les plus utilisés.

1-2 - La fonctionnalité principale du navigateur

Le but principal d'un navigateur est de présenter la ressource Web que vous choisissez, en faisant la demande à partir du serveur et de l'afficher sur la fenêtre du navigateur. La ressource est généralement un document HTML, mais peut aussi être un PDF, une image ou un autre type. L'emplacement de la ressource est spécifié par l'utilisateur à l'aide d'une URI (Uniform Resource Identifier).

La façon dont le navigateur interprète et affiche les fichiers HTML est précisée dans les spécifications HTML et CSS. Ces spécifications sont maintenues par le l'organisation W3C (World Wide Web Consortium), organisation des normes du Web.

Pendant des années, les navigateurs ne respectaient qu'une partie de ces spécifications et développaient leurs propres extensions. Cela a causé de graves problèmes de compatibilité pour les concepteurs Web. Aujourd'hui, la plupart des navigateurs sont plus ou moins conformes à ces spécifications.

Les interfaces des navigateurs ont beaucoup d'éléments en commun. On y trouve :

- une barre d'adresse pour insérer l'URL ;

- des boutons « Précédent » et « Suivant » ;

- des options de marque-pages ;

- des boutons d'actualisation et d'arrêt pour se rafraîchir et arrêter le chargement des documents courants ;

- un bouton « Accueil » qui vous ramène à votre page d'accueil.

Curieusement, l'interface du navigateur n'est pas spécifiée dans une spécification formelle, il s'agit simplement de bonnes pratiques façonnées au fil des années d'expérience et par l'imitation des autres navigateurs. La spécification HTML5 ne définit pas les éléments d'interface que doit avoir un navigateur, mais énumère certains éléments communs. Parmi celles-ci, la barre d'adresse, la barre d'état et la barre d'outils. Il y a bien sûr les caractéristiques propres à un navigateur spécifique comme le gestionnaire de téléchargements de Firefox.

1-3 - La structure haut niveau d'un navigateur

Les principaux composants (voir ) d'un navigateur sont :

1. L'interface utilisateur - ce qui inclut la barre d'adresse, les boutons avant et arrière, le menu de marque-page, etc. En fait, chacune des parties affichées par le navigateur excepté la fenêtre principale dans laquelle vous voyez la page demandée ;

2. Le moteur du navigateur - contrôle les actions entre l'interface et le moteur de rendu ;

3. Le moteur de rendu - responsable de l'affichage du contenu demandé. Par exemple, si le contenu demandé est au format HTML, il est chargé d'analyser le code HTML et CSS et d'afficher le contenu analysé à l'écran ;

4. Le réseau - utilisé pour les appels réseau, comme les requêtes HTTP. Il possède une interface indépendante de la plateforme et en dessous des implémentations pour chaque plateforme ;

5. L'interface utilisateur - utilisée pour dessiner des widgets de base comme des listes déroulantes et des fenêtres. Le navigateur expose une interface générique qui n'est pas spécifique à la plateforme. En dessous, il utilise l'interface utilisateur du système d'exploitation ;

6. L'interpréteur JavaScript - utilisé pour analyser et exécuter le code JavaScript ;

7. Le stockage de données - il s'agit d'une couche de persistance. Le navigateur doit enregistrer toutes sortes de données sur le disque dur, par exemple, des cookies. La nouvelle spécification HTML (HTML5) définit le terme « base de données Web », qui est un système complet (bien que léger) de base de données dans le navigateur.

Figure 1 : les composants principaux d'un navigateur

Il est important de noter que Chrome, contrairement à la plupart des navigateurs, crée plusieurs instances du moteur de rendu - une pour chaque onglet. Chaque onglet est un processus distinct.

2 - Le moteur de rendu

La responsabilité du moteur de rendu est importante… Le rendu, c'est l'affichage des contenus demandés sur l'écran du navigateur.

Par défaut, le moteur de rendu peut afficher des documents HTML, XML et des images. Il peut afficher d'autres types avec un plug-in (ou extension de navigateur), par exemple, PDF s'affiche en utilisant un plug-in de visualisation de PDF. Cependant, dans ce chapitre, nous nous concentrerons sur le cas d'utilisation principal : affichage de HTML et d'images qui sont formatés à l'aide de CSS.

2-1 - Les moteurs de rendu

Nos navigateurs de références Firefox, Chrome et Safari sont construits sur deux moteurs de rendu. Firefox utilise Gecko un moteur « fait maison » de Mozilla. Safari et Chrome utilisent Webkit.

WebKit est un moteur de rendu Open Source qui a commencé comme un moteur de plateforme Linux et a été modifié par Apple pour soutenir Mac et Windows. Voir webkit.org pour plus de détails.

2-2 - Le flux principal

Le moteur de rendu commencera à obtenir le contenu du document demandé mis en réseau. Ce sera généralement effectué en morceaux de 8 K.

Après c'est le flux de base du moteur de rendu :

Figure 2 : flux de base du moteur

Le moteur de rendu commencera à faire l'analyse du document HTML et activera les mots-clés aux nœuds de dans un arbre appelé « arbre de contenu ». Il analysera les données de style, à la fois dans les fichiers CSS externes et les éléments de style. L'information de style ainsi que des instructions visuelles dans le code HTML seront utilisées pour créer un autre arbre : « ».

L'arbre de rendu contient des rectangles avec des attributs visuels comme les couleurs et les dimensions. Les rectangles sont dans le bon ordre pour être affichés sur l'écran.

Après la construction de l'arbre de rendu, il passe par un processus de « ». Ceci signifie de donner les coordonnées exactes où il devrait apparaître sur l'écran. L'étape suivante : l'arbre de rendu sera traversé et chaque nœud sera dessiné en utilisant la couche d'arrière-plan de l'interface utilisateur.

Il est important de comprendre que c'est un processus graduel. Pour une meilleure expérience utilisateur, le moteur de rendu essayera d'afficher le contenu sur l'écran dès que possible. Il n'attendra pas que tout le code HTML soit analysé avant de commencer à présenter l'arbre de rendu. Les parties du contenu seront analysées et affichées, tandis que le processus se poursuit avec le reste de la page qui continue à arriver du réseau.

2-3 - Exemples des flux principaux

Figure 3 : flux principal du moteur Webkit

Figure 4 : flux principal du moteur de rendu Gecko de Mozilla

À partir des figures 3 et 4, vous pouvez voir que, bien que Webkit et Gecko utilisent une terminologie légèrement différente, le flux est essentiellement le même.

Gecko appelle l'arbre des éléments formatés visuellement un « Frame tree » (arbre de vue). Chaque élément est un cadre. Webkit utilise le terme « arbre de rendu » et il se compose de « Render Objects » (objets de rendu). Webkit utilise le terme « layout » (présentation) pour le placement des éléments, pendant que le Gecko l'appelle « Reflow » (ré-écoulement ). « Attachment » (attachement) est le terme de Webkit pour relier les nœuds de DOM et les informations visuelles afin de créer l'arbre de rendu. Une différence non sémantique mineure est que Gecko a une couche supplémentaire entre le HTML et l'arbre DOM. Il est appelé « content sink » (lavabo de contenu) et est une usine pour faire des éléments DOM. Nous parlerons de chaque partie du flux.

3 - Analyse et construction de l'arbre DOM

3-1 - L'analyse - généralités

Comme l'analyse est un processus très important dans le moteur de rendu, nous irons un peu plus dans les détails. Commençons par une petite introduction sur l'analyse.

Analyser un document signifie le traduire en une structure qui fait sens - quelque chose que le code peut comprendre et utiliser. Le résultat de l'analyse est en général un arbre de nœuds qui représente la structure du document. Il est appelé un arbre d'analyse ou un arbre syntaxique.

Exemple - l'analyse de l'expression 2 + 3 - 1 pourrait retourner cet arbre :

Figure 5 : arbre de l'expression mathématique

3-1-1 - Les grammaires

L'analyse est basée sur les règles de syntaxe auxquelles le document obéit - la langue ou le format dans lequel il a été écrit. Chaque format que vous analysez doit avoir une grammaire déterministe constituée d'un vocabulaire et de règles de syntaxe. C'est ce que l'on appelle une grammaire sans contexte. Les langues humaines n'en sont pas et ne peuvent donc pas être analysées avec des techniques d'analyse classiques.

3-1-2 - La combinaison Parser - Lexer

L'analyse peut être séparée en deux processus - l'analyse lexicale et l'analyse syntaxique.

L'analyse lexicale est le processus de séparation de l'entrée en mots-clés. Les mots-clés sont le vocabulaire de la langue - la collection de blocs de construction valides. Pour un langage humain, il se compose de tous les mots qui apparaissent dans le dictionnaire de cette langue.

L'analyse syntaxique est l'application des règles de la syntaxe du langage.

Les analyseurs, généralement, divisent le travail entre les deux éléments - l'analyseur lexical (appelé lexer ou tokenizer en langue anglaise) qui est responsable de l'extraction des mots-clés depuis l'entrée et l'analyseur syntaxique qui est responsable de la construction de l'arbre syntaxique par l'analyse de la structure du document selon les règles de syntaxe du langage. L'analyseur lexical sait comment supprimer des caractères non pertinents tels que les espaces et des sauts de ligne.

Figure 6 : du document source à l'arbre du document

Le processus d'analyse est itératif. L'analyseur syntaxique demande généralement à l'analyseur lexical le prochain mot-clé et essaye de faire correspondre ce mot-clé avec l'une des règles de syntaxe. Si une règle est trouvée, un nœud correspondant au mot-clé sera ajouté à l'arbre d'analyse et l'analyseur syntaxique demandera un autre mot-clé.

Si aucune règle ne correspond, l'analyseur va stocker le mot-clé en interne et continuer à demander des mots-clés jusqu'à ce qu'une règle de correspondance avec les mots-clés stockés en interne soit trouvée. Si aucune règle n'est trouvée, alors l'analyseur déclenche une exception. Cela signifie que le document n'est pas valide et qu'il contient des erreurs de syntaxe.

3-1-3 - La traduction

Souvent, l'arbre d'analyse syntaxique n'est pas le produit final. L'analyse est souvent employée en traduction - la transformation du document d'entrée en un autre format. Un exemple est la compilation. Le compilateur qui compile un code source en code machine analyse d'abord le code avec un arbre d'analyse et traduit ensuite cet arbre dans un document code machine.

Figure 7 : étapes de compilation

3-1-4 - Exemple d'analyse

Dans la figure 5, nous avons construit un arbre d'analyse à partir d'une expression mathématique. Essayons de définir un langage mathématique simple et de voir le processus d'analyse.

Vocabulaire : notre langage peut inclure des nombres entiers ainsi que les signes plus et moins.

Syntaxe :

1. Les blocs de construction sont des expressions, des termes et des opérations ;

2. Notre langage peut comporter un nombre quelconque d'expressions ;

3. Une expression est définie comme un « terme » suivi d'une « opération » suivie par un autre terme ;

4. Une opération est un signe plus ou un signe moins ;

5. Un terme est un entier ou une expression.

Analysons l'entrée 2 + 3 - 1.

La première chaîne qui correspond à une règle est 2 et conformément à la règle N° 5, c'est un terme. La deuxième correspondance est 2 + 3 cela correspond à la troisième règle - un terme suivi d'une opération suivie par un autre terme. La prochaine correspondance ne sera atteinte qu'à la fin de l'entrée. 2 + 3 - 1 est une expression parce que nous savons déjà que 2 +3 est un terme et que nous avons un terme suivi par une opération suivie par un autre terme. 2 + + ne correspond à aucune règle et n'est donc pas une entrée valide.

3-1-5 - Définitions formelles du vocabulaire et de la syntaxe

Le vocabulaire est en général exprimé par des expressions régulières.

Par exemple, notre langage sera défini comme :

1. INTEGER :0|[1-9][0-9]*

2. PLUS : +

3. MINUS: -

Comme vous le voyez, les nombres sont définis par une expression régulière.

La syntaxe est généralement définie dans un format nommé BNF. Notre langage sera défini comme cela :

1. expression := term operation term

2. operation := PLUS | MINUS

3. term := INTEGER | expression

Nous avons dit qu'un langage peut être analysé par un analyseur normal si sa grammaire est une grammaire sans contexte. La définition intuitive d'une grammaire sans contexte est une grammaire qui peut entièrement être exprimée en format BNF. Pour une définition plus formelle voir l'article Wikipédiasur les grammaires sans contexte.

3-1-6 - Les types d'analyseurs

Il y a deux types d'analyseurs - les analyseurs de haut en bas (top down) et les analyseurs de bas en haut (bottom up). Une explication intuitive est que les analyseurs de haut en bas voient la structure de la syntaxe de haut niveau et tentent de faire correspondre l'entrée à l'une des règles. Les analyseurs de bas en haut commencent avec l'entrée et la transforment peu à peu en règles de syntaxe, à partir des règles de bas niveau jusqu'à ce que les règles de haut niveau soient remplies.

Voyons comment ces deux types d'analyseurs vont analyser notre exemple.

Un analyseur de haut en bas commencera à partir de la règle de niveau supérieur - il identifiera 2 + 3 comme une expression. Il identifiera alors 2 + 3 - 1 comme une expression (le processus d'identification de l'expression change pour correspondre avec les autres règles, mais le point de départ est la règle de plus haut niveau).

Un analyseur de bas en haut va lire l'entrée jusqu'à ce qu'une règle corresponde et il remplacera alors l'entrée correspondant à la règle. Et ainsi de suite jusqu'à la fin de l'entrée. L'expression partiellement identifiée est placée sur la pile analyseurs.

Pile

Entrée

2 + 3 - 1

terme

+ 3 - 1

terme opération

3 - 1

expression

- 1

expression opération

1

expression

Ce type d'analyseur de bas en haut est appelé analyseur à décalage-réduction, parce que l'entrée est décalée vers la droite (imaginez un pointeur pointant d'abord sur le début de l'entrée puis se déplaçant vers la droite) et est progressivement réduite à des règles de syntaxe.

3-1-7 - Générer des analyseurs automatiquement

Il existe des outils qui permettent de générer un analyseur syntaxique pour vous. Ils sont appelés générateurs d'analyseurs. Vous leur fournissez la grammaire de la langue, son vocabulaire et la syntaxe de ses règles, et ils génèrent un analyseur. La création d'un analyseur nécessite une connaissance approfondie de l'analyse et comme il n'est pas facile de créer à la main un analyseur optimisé, ces générateurs d'analyseurs sont donc très utiles.

Webkit utilise deux générateurs d'analyseur bien connus - Flex pour créer l'analyseur lexical et Bison pour la création de l'analyseur syntaxique (vous pourrez aussi les rencontrer avec les termes Lex et Yacc). L'entrée de Flex est un fichier contenant la définition des expressions régulières des mots-clés. L'entrée de Bison est constituée par les règles de syntaxe du langage au format BNF.

3-2 - L'analyseur HTML

Le rôle de l'analyseur HTML est d'analyser les balises HTML et de créer un arbre d'analyse.

3-2-1 - La définition de la grammaire HTML

Le vocabulaire et la syntaxe du langage HTML sont définis dans des spécifications créées par l'organisation W3C. La version actuelle est HTML4 et le travail sur le HTML5 est en cours.

3-2-2 - Ce n'est pas une grammaire sans contexte

Comme nous l'avons vu dans l'introduction sur l'analyse, la syntaxe grammaticale peut être définie de manière formelle en utilisant des formats tels que la BNF.

Malheureusement, la notion d'analyseur conventionnel ne s'applique pas au format HTML (je n'en ai pas parlé juste pour le plaisir, ils seront utilisés lors de l'analyse CSS et JavaScript). HTML ne peut pas être défini facilement avec la grammaire sans contexte dont ont besoin les analyseurs.

Il existe un document officiel pour définir HTML, une DTD (Document Type Definition), mais ce n'est pas une grammaire sans contexte.

Cela semble étrange à première vue ; HTML étant assez proche de XML. Il existe beaucoup d'analyseurs XML disponibles. Il y a une différence entre XML et HTML - XHTML, alors quelle est cette différence ?

La différence est que l'approche HTML est plus « clémente », elle vous permet d'omettre certaines balises qui sont ajoutées implicitement, ou encore le début ou la fin de la balise, etc. Dans l'ensemble c'est une syntaxe « souple », par opposition à la syntaxe « stricte » et « exigeante » de XML.

Apparemment, cette petite différence fait un monde de différence. D'une part, c'est la raison principale pour laquelle HTML est si populaire, il pardonne vos erreurs et facilite la vie aux auteurs Web. D'autre part, il est difficile d'écrire une grammaire formelle. Donc, pour résumer, HTML ne peut pas être analysé facilement, en tout cas pas par des analyseurs conventionnels, puisque sa grammaire n'est pas une grammaire sans contexte, ni par des analyseurs XML.

3-2-3 - La DTD HTML

La définition HTML est dans un format DTD. Ce format est utilisé pour définir les langues de la famille SGML. Le format contient les définitions de tous les éléments autorisés, leurs attributs et leur hiérarchie. Comme nous l'avons vu précédemment, la DTD HTML ne constitue pas une grammaire sans contexte.

Il existe quelques variantes de DTD. Le mode strict se conforme uniquement à la norme, mais d'autres modes supportent les formes utilisées par les navigateurs dans le passé. Le but est la rétrocompatibilité avec les anciens contenus. La DTD stricte actuelle se trouve ici : www.w3.org/TR/html4/strict.dtd.

3-2-4 - Le DOM

L'arbre de sortie, « l'arbre syntaxique », est constitué de nœuds éléments et attributs DOM. DOM est l'acronyme de « Document Object Model ». Il s'agit de la représentation « objet » du document HTML ainsi que l'interface des éléments HTML avec le monde extérieur comme JavaScript.

La racine de l'arbre est l'objet « document ».

Le DOM possède une relation quasiment un-à-un avec le balisage. Par exemple, ce code :

1. <html>

2. <body>

3. <p>

4. Hello World

5. </p>

6. <div> <img src="example.png"/></div>

7. </body>

8. </html>

Se traduirait par l'arbre DOM suivant :

Figure 8 : arbre DOM du langage exemple

Comme pour HTML, DOM est spécifié par l'organisation W3C , voir www.w3.org/DOM/DOMTR. C'est une spécification générique pour la manipulation de documents. Un document spécifique décrit les éléments propres à HTML. La définition HTML peut être trouvée ici : www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.html.

Quand je dis que l'arbre contient des nœuds DOM, je veux dire que l'arbre est constitué d'éléments qui mettent en œuvre l'une des interfaces DOM. Les navigateurs utilisent des implémentations concrètes qui ont d'autres attributs utilisés par le navigateur en interne.

3-2-5 - L'algorithme d'analyse

Comme nous l'avons vu auparavant, le langage HTML ne peut pas être analysé en utilisant des analyseurs de bas en haut ou de haut en bas.

Les raisons sont :

1. La nature indulgente du langage ;

2. Le fait que les navigateurs ont une tolérance traditionnelle aux erreurs pour supporter les cas bien connus de HTML non valide ;

3. Le processus d'analyse est réentrant. Habituellement, la source ne change pas pendant l'analyse, mais en HTML, des balises de script contenant document.write peuvent ajouter des entrées supplémentaires, de sorte que le processus d'analyse modifie en fait l'entrée.

Incapables d'utiliser les techniques traditionnelles d'analyse, les navigateurs doivent utiliser des analyseurs personnalisés pour lire le HTML.

L'algorithme d'analyse est décrit en détail par la spécification HTML5. L'algorithme se compose de deux étapes - la séparation des mots-clés et la construction de l'arbre.

La séparation est l'analyse lexicale, transformer l'entrée en mots-clés. Parmi les mots-clés HTML, on trouve les balises de début, de fin, les noms d'attributs et les valeurs d'attributs.

L'analyse lexicale reconnaît le mot-clé, le donne au constructeur de l'arbre et consomme le caractère suivant pour reconnaître le mot-clé suivant. Et ainsi de suite jusqu'à la fin de l'entrée.

Figure 9 : flux d'analyse HTML (issu de la spécification HTML5)

3-2-6 - L'algorithme de séparation

La sortie de l'algorithme est un mot-clé HTML. L'algorithme est exprimé comme une machine à états. Chaque état consomme un ou plusieurs caractères du flux d'entrée et met à jour l'état suivant en fonction de ces caractères. La décision est influencée par l'état courant et par l'état de la construction de l'arbre. Cela signifie que le même caractère consommé donnera des états de sortie différents en fonction de l'état courant. L'algorithme est trop complexe pour être décrit entièrement, alors voyons un exemple simple qui nous aidera à comprendre le principe.

Exemple simple, lire le code HTML suivant :

1. <html>

2. <body>

3. Hello world

4. </body>

5. </html>

L'état initial est « données ». Lorsque le caractère < est lu, l'état est changé en « ouvert ». Consommer un caractère provoque la création d'un « début de nom », l'état est changé en « nom ». L'état ne change pas jusqu'à ce que le caractère > soit lu, chaque caractère étant ajouté au nom. Dans notre cas, le mot-clé créé est html.

Lorsque le caractère > est atteint, le mot-clé courant est émis et l'état redevient « données ». La balise <body> sera traitée par le même processus. Pour l'instant, les balises html et body ont été émises. Nous sommes maintenant de retour à l'état « données ». Consommer le caractère H de Hello world va provoquer la création et l'émission d'un mot-clé caractère, cela continue jusqu'à ce que le < de </body> soit atteint. Il y aura émission d'un mot-clé caractère pour chaque caractère de Hello world.

Nous sommes maintenant de retour à l'état « ouvert ». Consommer le caractère suivant / provoquera la création d'un mot-clé balise de fin et le passage à l'état « nom ». Encore une fois nous restons dans cet état jusqu'à ce que nous atteignons >. Ensuite le mot-clé est émis et nous revenons à l'état « données ». L'entrée </html> sera traitée comme précédemment.

Figure 10 : séparation de l'entrée de l'exemple

3-2-7 - L'algorithme de construction de l'arbre

Lorsque l'analyseur est créé, l'objet Document est créé. Pendant la phase de construction de l'arbre, l'arbre DOM avec le Document dans sa racine est modifié et les éléments sont ajoutés. Chaque nœud émis par l'analyseur lexical est traité par le constructeur d'arbre. Pour chacun des mots-clés définis par la spécification DOM, un élément DOM sera créé. Sauf s'il est ajouté à l'arbre DOM, il est ajouté à une pile d'éléments ouverts. Cette pile est utilisée pour corriger les déséquilibres d'imbrication et les balises non fermées. L'algorithme est aussi décrit comme une machine à états. Les états sont appelés « modes d'insertion ».

Voyons le processus de construction de l'arbre pour cet exemple :

1. <html>

2. <body>

3. Hello world

4. </body>

5. </html>

L'entrée à l'étape de la construction de l'arbre est une séquence de mots-clés issus de l'analyse lexicale. Le premier état est l'état « initial ». La réception du mot-clé html provoquera le passage à l'état « avant html » et un nouveau traitement de ce mot-clé pour ce mode. Cela entraînera la création de l'élément HTMLHtmlElement qui sera ajouté à la racine de l'objet Document.

L'état sera alors changé en « avant head ». Nous recevons ensuite le mot-clé body. Un HTMLHeadElement est créé implicitement, bien que nous n'avons pas de mot-clé head et il est ajouté à l'arbre.

Nous passons maintenant de l'état « dans entête » à « après entête ». Le mot-clé body est traité à nouveau, un HTMLBodyElement est créé et inséré et l'état est ensuite modifié en « dans corps ».

Les mots-clés caractère de la chaîne « Hello world » sont maintenant reçus. Le premier va causer la création et l'insertion d'un nœud text et les autres caractères seront ajoutés à ce nœud.

La réception du mot-clé fin de corps va provoquer le changement d'état « après corps ». Nous allons maintenant recevoir la balise de fin html qui va changer l'état en « après après corps ». La réception du mot-clé fin de fichier termine l'analyse.

Figure 11 : construction de l'arbre de l'exemple html

3-2-8 - Les actions en fin d'analyse

À ce stade, le navigateur va marquer le document comme interactif et lancer l'analyse des scripts marqués comme « différés » - ceux qui doivent être exécutés après que le document a été analysé. L'état document sera alors réglé sur « terminé » et un événement « chargement » est déclenché.

Vous pouvez voir les algorithmes complets pour l'analyse et la construction des arbres dans la spécification HTML5.

3-2-9 - La tolérance aux erreurs des navigateurs

Vous n'aurez jamais une erreur « syntaxe invalide » avec une page HTML. Les navigateurs corrigent les contenus invalides et continuent.

Prenez ce code HTML par exemple :

1. <html>

2. <mytag>

3. </mytag>

4. <div>

5. <p>

6. </div>

7. Really lousy HTML

8. </p>

9. </html>

Je dois avoir violé environ un million de règles (mytag n'est pas une balise standard, mauvaise imbrication des balises p et div et plus encore), mais le navigateur le montre quand même correctement et ne se plaint pas. Ainsi, beaucoup de code de l'analyseur sert à corriger les erreurs des codeurs HTML.

La gestion des erreurs est tout à fait compatible avec les navigateurs, mais étonnamment, elle ne fait pas partie de la spécification HTML actuelle. Comme les boutons « Favoris », « Précédent » et « Suivant », c'est juste quelque chose qui s'est développé dans les navigateurs au fil des années. On connaît des constructions HTML invalides qui se répètent sur de nombreux sites et les navigateurs essayent de les résoudre d'une manière conforme avec les autres navigateurs.

La spécification HTML5 définit certaines de ces exigences. Webkit résume cela très bien dans les commentaires du début de la classe de l'analyseur HTML.

L'analyseur analyse l'entrée sous forme de mot-clé dans le document, en construisant l'arbre du document. Si le document est bien formé, son analyse est simple.

Malheureusement, nous avons à gérer de nombreux documents HTML qui ne sont pas bien formés de sorte que l'analyseur doit être tolérant avec les erreurs.

Nous devons faire attention au moins aux conditions d'erreur suivantes :

1. L'élément ajouté est explicitement interdit à l'intérieur de certaines balises extérieures. Dans ce cas, nous devrions fermer toutes les balises jusqu'à celle qui interdit l'élément et l'ajouter à la suite ;

2. Nous ne sommes pas autorisés à ajouter l'élément directement. Il se pourrait que la personne qui écrit le document ait oublié quelques balises entre les deux (ou que la balise entre les deux est en option). Ce pourrait être le cas avec les balises HTML suivantes : HEAD BODY TBODY TR TD LI (en ai-je oublié une ?) ;

3. Nous voulons ajouter un élément de bloc à l'intérieur d'un élément en ligne. Fermer tous les éléments en ligne jusqu'au bloc supérieur suivant ;

4. Si cela ne fonctionne pas, fermer les éléments jusqu'à être autorisé à ajouter l'élément ou ignorer la balise.

Voyons quelques exemples de tolérance aux erreurs de Webkit.

3-2-9-1 - </br> à la place de <br>

Certains sites utilisent </br> au lieu de <br>. De manière à être compatible avec IE et Firefox, Webkit traite cela comme un <br>.

Le code :

1. if (t->isCloseTag(brTag) && m_document->inCompatMode()) {

2. reportError(MalformedBRError);

3. t->beginTag = true;

4. }

Notez que la gestion d'erreur est interne, elle ne sera pas présentée à l'utilisateur.

3-2-9-2 - Une table parasite

Une table parasite est une table à l'intérieur d'une autre, mais pas à l'intérieur d'une cellule.

Comme dans cet exemple :

1. <table>

2. <table>

3. <tr><td>inner table</td></tr>

4. </table>

5. <tr><td>outer table</td></tr>

6. </table>

Webkit va changer la hiérarchie pour deux tables sœurs :

1. <table>

2. <tr><td>outer table</td></tr>

3. </table>

4. <table>

5. <tr><td>inner table</td></tr>

6. </table>

Le code :

1. if (m_inStrayTableContent && localName == tableTag)

2. popBlock(tableTag);

Webkit utilise une pile pour le contenu des éléments courants, il va dépiler la table interne de la table externe, ces tables seront désormais sœurs.

3-2-9-3 - Imbrication de formulaires

Dans le cas où un utilisateur met un formulaire à l'intérieur d'un autre, le deuxième est ignoré.

Le code :

1. if (!m_currentFormElement) {

2. m_currentFormElement = new HTMLFormElement(formTag, m_document);

3. }

3-2-9-4 - Une hiérarchie trop profonde

Ce commentaire parle de lui-même.

www.liceo.edu.mx est l'exemple d'un site qui permet d'atteindre un niveau d'imbrication d'environ 1500 balises <b>. Nous n'autoriserons pas plus de 20 balises imbriquées du même type avant de toutes les ignorer.

1. bool HTMLParser::allowNestedRedundantTag(const AtomicString& tagName)

2. {

3. unsigned i = 0;

4. for (HTMLStackElem* curr = m_blockStack;

5. i < cMaxRedundantTagDepth && curr && curr->tagName == tagName;

6. curr = curr->next, i++) { }

7. return i != cMaxRedundantTagDepth;

8. }

3-2-9-5 - Balises HTML ou body fermantes mal placées

Encore une fois, le commentaire parle de lui-même.

La prise en charge de HTML vraiment mal formé. Nous ne fermons jamais la balise body, car certaines pages Web stupides la ferment avant la fin réelle du document. Nous nous basons uniquement sur le end() pour tout fermer.

1. if (t->tagName == htmlTag || t->tagName == bodyTag )

2. return;

Ainsi, codeurs Web, méfiez-vous, à moins que vous ne souhaitiez apparaître comme un exemple dans un morceau de code de la tolérance aux erreurs de Webkit, écrivez un code HTML bien formé.

3-3 - L'analyse CSS

Vous rappelez-vous les concepts de l'analyse dans l'introduction ? Eh bien, contrairement à HTML, CSS est une grammaire sans contexte, elle peut être analysée à l'aide des types d'analyseurs décrits dans l'introduction. En fait, la spécification CSS définit sa grammaire lexicale et syntaxique.

Voyons quelques exemples.

La grammaire lexicale (le vocabulaire) est définie par les expressions régulières pour chaque mot-clé :

1. comment \/\*[^*]*\*+([^/*][^*]*\*+)*\/

2. num [0-9]+|[0-9]*"."[0-9]+

3. nonascii [\200-\377]

4. nmstart [_a-z]|{nonascii}|{escape}

5. nmchar [_a-z0-9-]|{nonascii}|{escape}

6. name {nmchar}+

7. ident {nmstart}{nmchar}*

8. "ident" is short for identifier, like a class name. "name" is an element id (that is referred by "#" )

La syntaxe de la grammaire est décrite en BNF :

1. ruleset

2. : selector [ ',' S* selector ]*

3. '{' S* declaration [ ';' S* declaration ]* '}' S*

4. ;

5. selector

6. : simple_selector [ combinator selector | S+ [ combinator? selector ]? ]?

7. ;

8. simple_selector

9. : element_name [ HASH | class | attrib | pseudo ]*

10. | [ HASH | class | attrib | pseudo ]+

11. ;

12. class

13. : '.' IDENT

14. ;

15. element_name

16. : IDENT | '*'

17. ;

18. attrib

19. : '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*

20. [ IDENT | STRING ] S* ] ']'

21. ;

22. pseudo

23. : ':' [ IDENT | FUNCTION S* [IDENT S*] ')' ]

24. ;

Explication, un jeu de règles possède la structure suivante :

1. div.error , a.error {

2. color:red;

3. font-weight:bold;

4. }

div.error et a.error sont des sélecteurs. La partie à l'intérieur des accolades contient les règles qui sont appliquées par cet ensemble de règles. Cette structure est définie formellement dans cette définition :

1. ruleset

2. : selector [ ',' S* selector ]*

3. '{' S* declaration [ ';' S* declaration ]* '}' S*

4. ;

Cela signifie qu'un ensemble de règles est un sélecteur ou éventuellement un ensemble de sélecteurs séparés par une virgule et des espaces (S représente un espace). Un jeu de règles contient des accolades et à l'intérieur, une déclaration ou éventuellement un certain nombre de déclarations séparées par un point-virgule. « déclaration » et « sélecteur» seront définis dans les définitions BNF suivantes.

3-3-1 - L'analyseur CSS de Webkit

Webkit utilise les générateurs d'analyseur Flex et Bison pour créer des analyseurs de grammaire CSS. Si vous vous souvenez de l'introduction aux analyseurs, Bison créé un analyseur de bas en haut décalage-réduction. Firefox utilise un analyseur de haut en bas écrit manuellement. Dans les deux cas, chaque fichier CSS est analysé dans une feuille de style, chaque objet contient des règles CSS. Les objets de la règle CSS contiennent des objets de sélection et de déclaration ainsi que d'autres objets de la grammaire CSS.

Figure 12 : analyse CSS

3-4 - L'ordre d'analyse des scripts et des feuilles de style

3-4-1 - Les scripts

Le modèle du Web est synchrone. Les auteurs s'attendent à ce que les scripts soient analysés et exécutés immédiatement lorsque l'analyseur atteint une balise <script>. L'analyse du document s'arrête jusqu'à ce que le script ait été exécuté. Si le script est externe, alors la ressource doit être d'abord récupérée sur le réseau, ceci est fait aussi de manière synchrone, l'analyse s'arrête jusqu'à ce que la ressource soit récupérée. C'était le modèle durant de nombreuses années et c'est également spécifié dans le HTML 4 et 5. Les auteurs pouvaient marquer le script comme « reporté » et donc il n'interrompait pas l'analyse du document, mais était exécuté après avoir été analysé. HTML5 ajoute une option pour marquer le script comme asynchrone de sorte qu'il soit analysé et exécuté par un thread différent.

3-4-2 - L'analyse spéculative

Aussi bien Webkit que Firefox font cette optimisation. Pendant l'exécution de scripts, un autre thread analyse le reste du document et découvre les autres ressources qui doivent être chargées depuis le réseau et les charge. De cette façon, les ressources peuvent être chargées sur des connexions parallèles et la vitesse globale est meilleure. Remarquez que l'analyse spéculative ne modifie pas l'arborescence DOM, ceci est laissé à la charge de l'analyseur principal, elle ne fait qu'analyser les références à des ressources externes, comme les scripts externes, les feuilles de style et les images.

3-4-3 - Les feuilles de style

D'autre part, les feuilles de style ont un modèle différent. Sur le plan conceptuel, il semble que puisque les feuilles de style ne changent pas l'arborescence DOM, il n'y a aucune raison d'attendre leur chargement et d'arrêter l'analyse syntaxique du document. Il y a tout de même un problème, pour les scripts demandant des informations de style durant l'analyse du document. Si le style n'est pas encore chargé et analysé, le script va obtenir des réponses fausses et apparemment, cela a causé beaucoup de problèmes. Cela semble être un cas limite, mais c'est tout à fait commun. Firefox bloque tous les scripts tant qu'une feuille de style est en cours de chargement et d'analyse. Webkit bloque les scripts seulement quand ils tentent d'accéder à certaines propriétés de style qui peuvent être affectées par des feuilles de style non encore chargées.

4 - La construction de l'arbre de rendu

Pendant que l'arbre DOM est en cours de construction, le navigateur construit aussi un autre arbre, l'arbre de rendu. Cet arbre est constitué des éléments visuels dans l'ordre dans lequel ils apparaissent. Il est la représentation visuelle du document. Le but de cet arbre est de permettre le dessin du contenu dans l'ordre correct.

Firefox appelle ces éléments dans l'arbre de rendu « images ». Webkit utilise le terme de rendu ou objet de rendu.

Un rendu sait faire sa mise en page et se peindre, lui et ses enfants.

La classe Webkits RenderObject, la classe de base des rendus, a la définition suivante :

1. class RenderObject{

2. virtual void layout();

3. virtual void paint(PaintInfo);

4. virtual void rect repaintRect();

5. Node* node; //the DOM node

6. RenderStyle* style; // the computed style

7. RenderLayer* containgLayer; //the containing z-index layer

8. }

Chaque rendu représente une zone rectangulaire correspondant le plus souvent à la boite du nœud CSS, tel que décrit par la spécification CSS2. Il contient des informations géométriques comme sa largeur, sa hauteur et sa position.

Le type de la boite est affecté par l'attribut de style display qui est pertinent pour le nœud (voir la section de ). Voici le code Webkit pour décider quel type de rendu devrait être créé pour un nœud DOM, selon l'attribut display.

1. RenderObject* RenderObject::createObject(Node* node, RenderStyle* style)

2. {

3. Document* doc = node->document();

4. RenderArena* arena = doc->renderArena();

5. ...

6. RenderObject* o = 0;

7. switch (style->display()) {

8. case NONE:

9. break;

10. case INLINE:

11. o = new (arena) RenderInline(node);

12. break;

13. case BLOCK:

14. o = new (arena) RenderBlock(node);

15. break;

16. case INLINE_BLOCK:

17. o = new (arena) RenderBlock(node);

18. break;

19. case LIST_ITEM:

20. o = new (arena) RenderListItem(node);

21. break;

22. ...

23. }

25. return o;

26. }

Le type de l'élément est également considéré, par exemple les champs de formulaires et les tables ont des cadres spéciaux.

Avec Webkit, si un élément veut créer un rendu spécial il remplace la méthode createRenderer. Le rendu pointe vers les objets de style qui contiennent les informations non géométriques.

4-1 - La relation entre l'arbre DOM et l'arbre de rendu

Les rendus correspondent aux éléments DOM, mais la relation n'est pas une relation un-à-un. Les éléments DOM non visuels ne sont pas insérés dans l'arbre de rendu. Par exemple, l'élément head. Les éléments dont l'attribut display est positionné à none n'apparaîtront pas dans l'arbre de rendu (en revanche, les éléments avec l'attribut display hidden seront présents dans l'arbre).

Il y a des éléments DOM qui correspondent à plusieurs objets visuels. Ce sont généralement des éléments de structure complexe qui ne peuvent être décrits par un simple rectangle. Par exemple, l'élément select possède trois rendus, un pour la zone d'affichage, un pour la zone de liste déroulante et un pour le bouton. De même, lorsque le texte est scindé en plusieurs lignes, car la largeur n'est pas suffisante pour tenir sur une seule, les nouvelles lignes seront ajoutées comme des rendus supplémentaires.

Un autre exemple de rendus multiples concerne le HTML mal formé. Selon les spécifications CSS, un élément inline doit contenir soit des blocs d'éléments ou alors seulement des éléments inline. En cas de contenu mixte, des rendus anonymes seront créés pour envelopper les éléments inline.

Certains objets rendus correspondent à un nœud DOM, mais pas au même endroit dans l'arbre. Les éléments flottants et ceux avec un positionnement absolu sont hors du flux, ils sont placés dans un endroit différent de l'arbre et appliqués au cadre réel. Leur emplacement réservé est là où ils auraient dû être.

Figure 13 : l

4-2 - La construction de l'arbre

Avec Firefox, la présentation s'enregistre comme un écouteur pour les mises à jour du DOM. La présentation délègue la création des cadres à FrameConstructor et le constructeur décide du style (voir ) et crée le cadre.

Avec Webkit le processus de résolution du style et la création du rendu sont appelés « attachement ». Chaque nœud DOM possède une méthode attach. L'attachement est synchrone, l'insertion d'un nœud dans l'arbre DOM appelle la méthode attach du nouveau nœud.

Le traitement des balises html et body aboutit à la construction de la racine de l'arbre de rendu. L'objet de rendu racine correspond à ce que la spécification CSS appelle le bloc conteneur, le bloc le plus haut qui contient tous les autres blocs. Ses dimensions sont celles de la fenêtre d'affichage du navigateur. Firefox l'appelle ViewPortFrame et Webkit l'appelle RenderView. C'est l'objet de rendu pointé par le document. Le reste de l'arbre est construit au fur et à mesure de l'insertion des nœuds DOM. Voir la spécification CSS2 sur le modèle de traitement.

4-3 - Calcul du style

Construire l'arbre de rendu nécessite le calcul des propriétés visuelles de chaque objet de rendu. Ceci est fait en calculant les propriétés de style de chaque élément.

Le style inclut des feuilles de style d'origines diverses, les éléments de style inline et les propriétés visuelles dans le code HTML (comme la propriété bgcolor). Celui-ci est traduit en correspondance des propriétés de style CSS.

Les origines des feuilles de style sont les feuilles de style par défaut du navigateur ; les feuilles de style fournies par l'auteur de la page et les feuilles de style de l'utilisateur (qui sont des feuilles de style fournies par l'utilisateur : les navigateurs vous permettent de définir votre style préféré. Avec Firefox, par exemple, cela est fait en plaçant une feuille de style dans le répertoire « Firefox profile »").

Le calcul du style fait apparaître quelques difficultés :

1. Les données d'un style peuvent être très importantes du fait de nombreuses propriétés de style, cela peut causer des problèmes de mémoire ;

2. Trouver les règles de correspondance pour chaque élément peut provoquer des problèmes de performances si ce n'est pas optimisé. Parcourir la liste de toutes les règles pour chaque élément est une lourde tâche. Les sélecteurs peuvent avoir une structure complexe qui peut provoquer le processus d'appariement au début sur un chemin apparemment prometteur et qui a fait ses preuves pour devenir ensuite futile, une autre voie doit alors être évaluée.

Par exemple, ce sélecteur composé :

Les règles du code suivant s'appliquent à un <div> qui est le descendant de trois <div>. Supposons que vous voulez vérifier si la règle s'applique pour un élément <div> donné. Vous choisissez un certain chemin dans l'arbre pour vérification. Vous pouvez avoir besoin de parcourir toute l'arborescence de nœuds juste pour voir qu'il y a seulement deux <div> et que la règle ne s'applique donc pas. Vous devez alors essayer d'autres chemins dans l'arbre.

1. div div div div{

2. ...

3. }

3. L'application des règles implique des règles de cascade assez complexes qui définissent leur hiérarchie.

Voyons comment les navigateurs font face à ces problèmes.

4-3-1 - Le partage des données de style

Les nœuds de Webkit référencent des objets style (RenderStyle) Ces objets peuvent être partagés par les nœuds sous certaines conditions. Les nœuds sont frères ou cousins et :

1. Les éléments doivent être dans le même état en ce qui concerne la souris (par exemple, on ne peut être en mode :hover si l'autre ne l'est pas) ;

2. Aucun élément ne doit avoir un identifiant ;

3. Les noms de balises doivent correspondre ;

4. Les attributs de classe doivent correspondre ;

5. L'ensemble des attributs associés doivent être identiques ;

6. L'état du lien doit correspondre ;

7. L'état du focus doit correspondre ;

8. Aucun élément ne doit être affecté par un sélecteur d'attribut ; affecté est défini comme ayant une correspondance avec un sélecteur contenant un sélecteur d'attribut quelle que soit sa position dans le sélecteur global ;

9. Il ne doit pas y avoir d'attribut de style sur les éléments ;

10. Il ne doit y avoir aucun sélecteur d'enfants. WebCore utilise simplement un commutateur global lorsqu'un sélecteur d'enfant est rencontré et désactive le partage de style pour le document entier quand ils sont présents. Cela comprend le sélecteur + et les sélecteurs comme :first-child et :last-child.

4-3-2 - L'arbre des règles de Firefox

Firefox dispose de deux arbres supplémentaires pour un calcul plus facile du style, l'arbre des règles et l'arbre de contexte de style. Webkit a aussi des objets de style, mais ils ne sont pas stockés dans un arbre, comme l'arbre de contexte de style, seul le nœud DOM pointe sur son style.

Figure 14 : l'arbre de contexte de style de Firefox

Les contextes de style contiennent des valeurs finales. Les valeurs sont calculées en appliquant toutes les règles d'appariement dans le bon ordre et en effectuant les manipulations qui les transforment de valeurs logiques à valeurs concrètes. Par exemple, si la valeur logique est le pourcentage par rapport à l'écran, il sera calculé et transformé en unités absolues. L'idée derrière l'arbre des règles est très intelligente. Elle permet de partager ces valeurs entre les nœuds pour éviter de les calculer à nouveau. Cela économise aussi de l'espace.

L'ensemble des règles adaptées sont stockées dans un arbre. Les nœuds du bas d'un chemin sont prioritaires. L'arbre contient tous les chemins pour la correspondance de règles qui ont été trouvées. Le stockage des règles se fait paresseusement. L'arbre n'est pas calculé au début de chaque nœud, mais à chaque fois qu'un style de nœud doit être calculé, le chemin calculé est ajouté à l'arbre.

L'idée est de voir l'arbre des chemins comme des mots dans un dictionnaire. Disons que nous avons déjà calculé cet arbre règle :

Supposons que nous devions faire correspondre des règles pour un autre élément dans l'arborescence de contenu et que nous trouvons que les règles appariées (dans le bon ordre) sont B - E - I. Nous avons déjà ce chemin dans l'arbre, car nous avons déjà calculé le trajet A - B - E - I - L. Nous avons maintenant moins de travail à faire.

Voyons comment cet arbre nous permet d'économiser du travail.

4-3-2-1 - La division par structure

Les contextes de style sont divisés en structures. Ces structures contiennent des informations de style pour une certaine catégorie comme la bordure ou la couleur. Toutes les propriétés dans une structure sont héritées ou non héritées. Les propriétés héritées sont des propriétés qui ne sont pas définies par l'élément, elles sont héritées du parent. Les propriétés non héritées (appelées propriétés « remise à zéro ») utilisent des valeurs par défaut si elles ne sont pas définies.

L'arbre nous aide en mettant en cache les structures (contenant les valeurs finales calculées) dans l'arborescence. L'idée est que si le nœud du bas n'a pas fourni une définition pour une structure alors une structure mise en cache pour un nœud supérieur peut être utilisée.

4-3-2-2 - Calculer les contextes de style en utilisant l'arbre des règles

Lors du calcul du contexte de style pour un élément donné, on calcule d'abord un chemin dans l'arbre des règles ou on en utilise un existant. Nous commençons alors par appliquer les règles dans le chemin pour combler les structures dans notre nouveau contexte style. Nous commençons à la partie basse du chemin, celui avec la plus haute priorité (généralement le sélecteur le plus spécifique) et parcourir l'arborescence jusqu'à ce que notre structure soit pleine. S'il n'y a pas de spécification pour la structure dans ce nœud règle, alors nous pouvons grandement optimiser, on remonte l'arborescence jusqu'à ce qu'on trouve un nœud qui satisfait pleinement et nous pointons tout simplement dessus, c'est la meilleure optimisation, la structure entière est partagée. Cela permet d'économiser le calcul des valeurs finales et la mémoire.

Si nous trouvons des définitions partielles, nous remontons l'arbre jusqu'à ce que la structure soit complète.

Si nous n'avons pas trouvé de définitions de notre structure, alors, dans le cas où la structure est de type « hérité », nous pointons vers la structure du parent dans l'arborescence des contextes, dans ce cas, nous avons également réussi à partager la structure. Si c'est une structure « remise à zéro », alors les valeurs par défaut sont utilisées.

Si le nœud le plus spécifique ajoute des valeurs, alors nous devons faire quelques calculs supplémentaires pour la transformer en valeurs réelles. Ensuite nous mettons en cache le résultat dans le nœud de l'arbre de sorte qu'il puisse être utilisé par des enfants.

Dans le cas où un élément a un frère qui pointe vers le nœud de l'arbre, alors le contexte de style complet peut être partagé entre eux.

Voyons un exemple, supposons que nous ayons ce HTML :

1. <html>

2. <body>

3. <div class="err" id="div1">

4. <p>

5. this is a <span class="big"> big error </span>

6. this is also a

7. <span class="big"> very big error</span> error

8. </p>

9. </div>

10. <div class="err" id="div2">another error</div>

11. </body>

12. </html>

Et les règles suivantes :

1. div {margin:5px;color:black}

2. .err {color:red}

3. .big {margin-top:3px}

4. div span {margin-bottom:4px}

5. #div1 {color:blue}

6. #div2 {color:green}

Pour simplifier les choses, disons que nous devons remplir seulement deux structures, la structure couleur et la structure marge. La structure couleur contient un seul membre, la couleur. La structure marge contient les quatre côtés.

L'arbre de règles résultant ressemblera à ceci (les nœuds sont identifiés par le nom du nœud et le numéro de la règle sur laquelle ils pointent) :

Figure 15 : l'arbre des règles

L'arbre des contextes ressemblera à ceci (le nom de nœud est le nom de règle générale vers laquelle il pointe) :

Figure 16 : l'arbre des contextes

Supposons que nous analysions le code HTML et que nous arrivions à la deuxième balise <div>. Nous devons créer un cadre de style pour ce nœud et remplir ses structures style.

Nous allons faire correspondre les règles et découvrir que les règles correspondantes pour le <div> sont 1, 2 et 6. Cela signifie qu'il y a déjà un chemin d'accès existant dans l'arborescence que notre élément peut utiliser, nous avons juste besoin d'ajouter un autre nœud pour la règle 6 (le nœud F dans l'arbre de règles).

Nous allons créer un contexte de style et le mettre dans l'arbre des contextes. Le nouveau contexte de style va pointer vers le nœud F dans l'arbre de règles.

Nous avons maintenant besoin de remplir les structures du style. Nous allons commencer par remplir la structure de marge. Puisque la règle du nœud F ne modifie pas la structure de marge, nous pouvons remonter dans l'arbre jusqu'à ce qu'on trouve une structure en cache calculée dans un nœud d'insertion précédent et l'utiliser. Nous allons la trouver avec le nœud B, qui est le nœud le plus élevé avec les règles de marge spécifiées.

Nous avons une définition de la structure couleur, donc on ne peut pas utiliser une structure en cache. Puisque la couleur n'a qu'un attribut, nous n'avons pas besoin d'aller dans l'arbre pour en remplir d'autres. Nous allons calculer la valeur finale (convertir la chaîne en RVB, etc.) et mettre en cache la structure calculée sur ce nœud.

Le travail sur le deuxième élément <span> est encore plus facile. Nous faisons correspondre les règles et arrivons à la conclusion qu'il pointe sur la règle G, comme le <span> précédent. Puisque ce sont des frères qui pointent vers le même nœud, nous pouvons partager le contexte de style tout simplement en pointant dans le contexte du <span> précédent.

Pour les structures qui contiennent des règles qui sont héritées du parent, la mise en cache se fait sur l'arbre de contexte (la propriété de couleur est en fait héritée, mais Firefox la traite comme « remise à zéro » et la met en cache sur l'arbre de règles).

Par exemple, si nous ajoutons des règles de polices dans un paragraphe :

1. p {font-family:Verdana;font size:10px;font-weight:bold}

Alors, l'élément de paragraphe, qui est un enfant de <div> dans l'arbre de contexte, aurait pu partager la structure de police de son parent. Cela serait le cas si aucune règle de police n'avait été spécifiée pour le paragraphe.

Avec Webkit, qui ne dispose pas d'un arbre de règles, les déclarations correspondantes sont traversées quatre fois. En premier, les propriétés non importantes (propriétés qui doivent être appliquées d'abord parce que d'autres dépendent d'elles, comme l'affichage) sont appliquées, ensuite, les propriétés importantes, puis les propriétés normales et enfin les propriétés normales pour les règles importantes. Cela signifie que les propriétés qui apparaissent plusieurs fois seront résolues selon l'ordre de cascade correct. C'est le dernier qui l'emporte.

Donc, pour résumer, le partage des objets style (tout ou partie des structures à l'intérieur) résout les problèmes 1 et 3. L'arbre de règles Firefox contribue également à l'application des propriétés dans le bon ordre.

4-3-3 - Manipuler les règles pour une recherche plus facile

Il existe plusieurs sources pour les règles de style :

- les règles CSS, soit dans des feuilles de style externes, soit dans des balises style :

1. p {color:blue}

- des attributs de style en ligne comme :

1. <p style="color:blue" />

- des attributs HTML visuels (qui sont mappés aux règles de style correspondantes) :

1. <p bgcolor="blue" />

Les deux derniers sont facilement adaptés à l'élément, car ils possèdent les attributs de style et attributs HTML qui peuvent être indexés en utilisant l'élément comme clé.

Comme indiqué précédemment dans le problème 2, la recherche de la règle CSS peut être plus compliquée. Pour résoudre cette difficulté, les règles sont manipulées pour en faciliter l'accès.

Après l'analyse de la feuille de style, les règles sont ajoutées à une des tables de hachage, en fonction de la sélection. Il y a des tables dont l'index est l'identifiant, le nom de classe, le nom de balise et une table générale pour tout ce qui ne rentre pas dans ces catégories. Si le sélecteur est un identifiant, la règle sera ajoutée à la table des identifiants, s'il s'agit d'une classe, elle sera ajoutée à la table des classes, etc.

Cette manipulation rend beaucoup plus facile la recherche des règles. Il n'est pas nécessaire de regarder dans chaque déclaration, nous pouvons extraire les règles applicables à un élément à partir des tables. Cette optimisation élimine 95 % et plus des règles, de sorte qu'elles ne devraient même pas être prises en compte lors du processus d'appariement ().

Voyons par exemple les règles de style suivantes :

1. p.error {color:red}

2. #messageDiv {height:50px}

3. div {margin:5px}

La première règle est insérée dans la table des classes. La seconde dans la table des identifiants et la troisième dans la table des balises.

Pour le morceau HTML suivant :

1. <p class="error">an error occurred </p>

2. <div id=" messageDiv">this is a message</div>

Nous allons d'abord essayer de trouver des règles pour l'élément p. La table des classes contiendra une clé error en vertu de laquelle la règle de p.error est trouvée. La balise div aura une règle qui correspond dans la table des identifiants (la clé est l'identifiant) et la table des balises. Ainsi, le travail qui reste à faire est de savoir quelles sont les règles qui ont été extraites par les index qui correspondent vraiment.

Par exemple, si la règle de la div est :

1. table div {margin:5px}

elle sera toujours extraite de la table des balises, parce que la clé est le sélecteur de droite, mais elle ne correspondrait pas à notre élément div, qui n'a pas de parent de type table.

À la fois Webkit et Firefox font cette manipulation.

4-3-4 - Appliquer les règles dans l'ordre correct

L'objet style a des propriétés correspondant à chaque attribut visuel (tous les attributs CSS, mais plus génériques). Si la propriété n'est définie par aucune des règles adaptées, alors certaines propriétés peuvent être héritées du style de l'élément parent. D'autres propriétés ont des valeurs par défaut.

Le problème commence lorsqu'il y a plus d'une définition, voici l'ordre pour résoudre ce problème.

4-3-4-1 - L'ordre des feuilles de style

La déclaration d'une propriété de style peut apparaître dans plusieurs feuilles de style et plusieurs fois à l'intérieur d'une même feuille de style. Cela signifie que l'ordre d'application des règles est très important. C'est ce qu'on appelle la "cascade" des commandes. Selon la spécification CSS2, l'ordre en cascade est (de bas en haut) :

1. Les déclarations du navigateur ;

2. Les déclarations normales de l'utilisateur ;

3. Les déclarations normales de l'auteur ;

4. Les déclarations importantes de l'auteur ;

5. Les déclarations importantes de l'utilisateur ;

Les déclarations du navigateur sont les moins importantes et celles de l'utilisateur supplantent celles de l'auteur uniquement si ces déclarations sont importantes. Les déclarations de même importance sont triées par et par ordre dans lequel elles apparaissent. Les attributs HTML visuels sont convertis en déclarations CSS. Ils sont traités comme des règles d'auteur de faible priorité.

4-3-4-2 - La spécificité

Le sélecteur de spécificité est défini par les spécifications CSS2 comme cela :

- compter 1 si la déclaration est issue d'un attribut style plutôt qu'une règle d'un sélecteur, 0 sinon (= a) ;

- compter le nombre d'attributs identifiant dans le sélecteur (= b) ;

- compter le nombre d'autres attributs et de pseudoclasse dans le sélecteur (= c) ;

- compter le nombre de noms d'éléments et de pseudoéléments dans le sélecteur (= d) ;

La concaténation des quatre nombres a-b-c-d (dans un système de numération à base large) donne la spécificité.

La base que vous devez utiliser est définie par le nombre le plus élevé que vous avez dans l'une des catégories.

Par exemple, si a = 14, vous pouvez utiliser la base hexadécimale. Dans le cas peu probable où vous aurez a = 17, vous aurez besoin d'une base 17. La situation peut se produire avec un sélecteur comme ceci : html body div div p ... (17 balises dans votre sélection, peu probable).

Quelques exemples :

1. * {} /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */

2. li {} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */

3. li:first-line {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */

4. ul li {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */

5. ul ol+li {} /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */

6. h1 + *[rel=up]{} /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */

7. ul ol li.red {} /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */

8. li.red.level {} /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */

9. #x34y {} /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */

10. style="" /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */

4-3-4-3 - Trier les règles

Une fois que les règles sont trouvées, elles sont triées d'après les règles de cascade. Webkit utilise un tri à bulles pour les petites listes et un tri par fusion pour les grandes. Webkit met en œuvre le tri en remplaçant l'opérateur « > » pour les règles :

1. static bool operator >(CSSRuleData& r1, CSSRuleData& r2)

2. {

3. int spec1 = r1.selector()->specificity();

4. int spec2 = r2.selector()->specificity();

5. return (spec1 == spec2) : r1.position() > r2.position() : spec1 > spec2;

6. }

4-4 - Processus graduel

Webkit utilise un indicateur qui marque les feuilles de style haut niveau (y compris les règles @imports) chargées. Si le style n'est pas complètement chargé lors de l'attachement, des jokers sont utilisés et le document est marqué. Ils seront recalculés une fois les feuilles de style chargées.

5 - La mise en page

Quand le rendu est créé et ajouté à l'arbre, il n'a ni position ni taille. Calculer ces valeurs est appelé « mise en page » ou « refusion ».

HTML utilise un modèle de mise en page basé sur une fusion, ce qui signifie que, la plupart du temps, il est possible d'en calculer la géométrie en une seule passe. Les éléments qui sont ajoutés « dans le flux » n'affectent généralement pas la géométrie des éléments qui y étaient déjà, de telle sorte que la mise en page se fait de gauche à droite et de haut en bas pour tout le document. On compte quelques exceptions - par exemple, le rendu des tableaux HTML peut prendre plusieurs passes (3.5).

Le système de coordonnées est relatif au cadre principal. Les coordonnées sont initialisées en haut à gauche.

La mise en page est un processus récursif. Il commence à la racine du moteur de rendu, ce qui correspond à l'élément <html> du document HTML. Elle continue de manière récursive dans une partie ou toute la hiérarchie des cadres, en calculant les informations géométriques pour chaque moteur de rendu qui le requiert.

La position du moteur de rendu racine est (0, 0) et ses dimensions correspondent à la zone d'affichage - la part visible de la fenêtre du navigateur.

Tous les moteurs de rendu ont une méthode de « mise en page » ou de « refusion », chacun invoque la méthode de mise en page de ses enfants quand il en a besoin.

5-1 - Le système du bit sale

Pour ne pas effectuer à nouveau une mise en page complète à chaque petit changement, les navigateurs utilisent un système de « bit sale ». Un moteur de rendu qui est changé ou ajouté se marque ainsi que ses enfants comme « sales » (ils doivent être remis en page).

Généralement, les navigateurs distinguent deux drapeaux : « sale » et « enfants sales ». Ce dernier signifie que, bien que le moteur de rendu lui-même corresponde toujours au contenu HTML, il y a au moins un enfant qui doit être remis en page.

5-2 - Mise en page globale et incrémentale

La mise en page peut s'effectuer sur tout l'arbre de rendu - on l'appelle « mise en page globale ». Cela peut arriver suite à :

1. Un changement dans le style global, qui affecte tout le contenu, comme un changement de la taille de police ;

2. Un redimensionnement de la fenêtre.

La mise en page peut également être incrémentale, auquel cas seuls les éléments sales seront remis en page (ce qui peut causer quelques dommages, qui causeront des remises en page supplémentaires).

Cette mise en page incrémentale est déclenchée (de manière asynchrone) quand les moteurs de rendu sont sales, par exemple quand de nouveaux objets sont ajoutés à la fin de l'arbre de rendu après que du contenu supplémentaire est arrivé du réseau et est ajouté à l'arbre DOM.

Figure 17 : mise en page incrémentale, seuls les moteurs de rendu sales et leurs enfants sont mis en page ou non

5-3 - Mise en page synchrone ou non

La mise en page incrémentale est effectuée de manière asynchrone. Firefox met en file d'attente les « commandes de refusion » pour des mises en page incrémentales et un planificateur lance l'exécution par lots de ces commandes. WebKit dispose aussi d'un minuteur qui effectue une mise en page incrémentale - l'arbre est traversé et les moteurs de rendu sales sont remis en page.

Les scripts qui demandent des informations sur le style, comme offsetHeight, peuvent déclencher une mise en page incrémentale synchrone.

La mise en page globale est généralement effectuée de manière synchrone.

De temps en temps, la mise en page est demandée par une fonction de rappel après une mise en page initiale parce que certains attributs, comme la position du défilement, ont changé.

5-4 - Optimisations

Quand une mise en page est déclenchée par un redimensionnement ou un changement dans la position d'un élément (non dans sa taille), les dimensions des éléments affectés ne sont pas recalculées, elles sont reprises d'un cache.

Dans certains cas, seul un sous-arbre est modifié, la mise en page ne repart pas de la racine. Ceci peut arriver dans des cas où le changement est local et n'affecte pas son environnement, comme de l'insertion de texte dans des champs (sinon, chaque appui sur une touche du clavier aurait relancé une mise en page depuis la racine).

5-5 - Le processus de mise en page

La mise en page suit généralement cette procédure :

1. L'élément parent détermine sa propre largeur ;

2. Le parent passe à ses enfants et :

1. Place l'élément enfant (il définit ses positions en x et y),

2. Appelle la mise en page de l'enfant au besoin (s'ils sont sales, si la mise en page est globale ou pour toute autre raison) ;

3. Le parent utilise les hauteurs cumulées de ses enfants et les hauteurs des marges internes et externes pour définir sa propre hauteur (cette information sera utilisée par le moteur de rendu de son parent) ;

4. Le bit sale est mis à une valeur fausse.

Firefox utilise un objet d'état (nsHTMLReflowState) comme paramètre à la mise en page (appelée reflow). Entre autres, l'état inclut la largeur du parent.

La sortie de la mise en page de Firefox est un objet de métrique (nsHTMLReflowMetrics) qui contient la hauteur calculée du moteur de rendu.

5-6 - Calcul de la largeur

La largeur d'un élément est calculée en utilisant la largeur du bloc conteneur, la propriété width du style du moteur, les marges et bordures.

Par exemple, la largeur de l'élément suivant :

1. <div style="width:30%"/>

serait calculée par WebKit comme ceci (méthode calcWidth de la classe RenderBox) :

- la largeur du contenu est le maximum de la propriété availableWidth du conteneur et de zéro. availableWidth, dans ce cas, est la valeur calculée comme :

1. clientWidth() - paddingLeft() - paddingRight()

- clientWidth et clientHeight représentent l'intérieur d'un objet, en dehors de sa bordure et de sa barre de défilement ;

- la largeur des éléments est donnée par l'attribut width du style. Elle est calculée comme une valeur absolue avec le pourcentage de la largeur du conteneur ;

- les bordures horizontales et les marges sont alors ajoutées.

Jusqu'ici, WebKit calculait la « largeur préférée ». Il faut à présent calculer les maximum et minimum.

Si la largeur préférée est supérieure à la largeur maximale, le maximum sera utilisé. Si elle est plus petite que la largeur minimale (la plus petite unité incassable), le minimum sera utilisé.

Les valeurs sont mises en cache, au cas où une mise en page serait requise sans que la largeur change.

5-7 - Passage à la ligne

Quand un élément, au beau milieu de sa mise en page, décide qu'il doit passer à la ligne, il s'arrête et dit à son parent qu'il a besoin de passer à la ligne. Le parent crée alors des rendus supplémentaires et leur demande d'effectuer une mise en page.

6 - Le dessin

Dans l'étape de dessin, l'arbre de rendu est parcouru et la méthode « dessiner » des rendus est appelée pour afficher le contenu à l'écran. Le dessin utilise l'infrastructure de l'interface utilisateur.

6-1 - Global et incrémental

Comme l'affichage, le dessin peut également être global, l'arbre entier est dessiné, ou incrémental. Dans le dessin incrémental, certains rendus changent d'une façon qui n'affecte pas l'arbre entier. Le rendu changé invalide son rectangle sur l'écran. L'OS le voit alors comme une « région sale » et génère un événement de « dessin ». L'OS le fait intelligemment et fusionne plusieurs régions en une seule. Sous Chrome c'est un peu plus compliqué, car le rendu est dans un processus différent du processus principal. Chrome simule le comportement de l'OS dans une certaine mesure. La couche présentation écoute ces événements et délègue le message à la racine du rendu. L'arbre est parcouru jusqu'à ce qu'un rendu pertinent soit atteint. Il se redessinera de lui-même (et généralement ses enfants avec).

6-2 - L'ordre de dessin

CSS2 définit l'ordre du processus de dessin. C'est en fait l'ordre dans lequel les éléments sont empilés dans la pile des contextes. Cet ordre affecte le dessin puisque les piles sont dessinées de l'arrière vers l'avant. L'ordre d'empilement d'un bloc de rendu est :

1. Couleur d'arrière-plan ;

2. Image d'arrière-plan ;

3. Bordures ;

4. Enfants ;

5. Contour.

6-3 - Liste d'affichage de Firefox

Firefox va au-delà de l'arbre de rendu et construit une liste d'affichage pour les rectangles dessinés. Elle contient les rendus pertinents pour ces rectangles dans le bon ordre de dessin (couleur d'arrière-plan, puis bordures, etc.). De cette façon l'arbre n'a besoin d'être traversé qu'une seule fois pour être redessiné au lieu de plusieurs, dessiner tous les arrières-plans, puis toutes les images, puis toutes les bordures, etc.

Firefox optimise le processus en n'ajoutant pas les éléments qui seront cachés, comme les éléments placés derrière un élément opaque.

6-4 - Stockage des rectangles sous Webkit

Avant de redessiner, Webkit stocke les anciens rectangles en bitmap. Il redessine ensuite uniquement les différences entre les rectangles stockés et les nouveaux.

7 - Les changements dynamiques

Les navigateurs essayent de faire le moins d'actions possible en réponse à un changement. Ainsi, le changement de couleur d'un élément aura pour effet de seulement repeindre cet élément. La modification de la position de l'élément aura pour effet de repeindre cet élément, ses enfants et peut-être aussi ses frères et sœurs. L'ajout d'un nœud DOM provoquera le dessin de ce nœud. Des changements majeurs, comme la taille de police de l'élément html, entraîneront l'invalidation des caches et demanderont le réarrangement et de redessin de l'arbre complet.

8 - Les threads du moteur de rendu

Le moteur de rendu est mono thread. Presque tout, sauf l'exploitation du réseau, se passe dans un seul thread. Dans Firefox et Safari c'est le thread principal du navigateur. Dans Chrome c'est le thread de processus onglet principal.

L'exploitation du réseau peut être effectuée par plusieurs threads en parallèle. Le nombre de connexions en parallèle est limité (généralement deux à six connexions. Firefox 3, par exemple, en utilise six).

8-1 - La boucle de messages

Le thread principal du navigateur est une boucle d'événements. C'est cette boucle infinie qui rend le processus plus vivant. Il attend des événements (comme une mise en page ou les événements de dessin) et les traite. Voici le code de Firefox pour la boucle principale :

1. while (!mExiting)

2. NS_ProcessNextEvent(thread);

9 - Le modèle visuel CSS2

9-1 - La toile (canevas)

Selon la spécification CSS2, le terme toile désigne « l'espace où la structure de format est rendue », où le navigateur dessine le contenu. Les dimensions de cette toile sont infinies, mais les navigateurs choisissent une largeur initiale basée sur les dimensions de la fenêtre.

Selon www.w3.org/TR/CSS2/zindex.html, cette toile, si elle est contenue dans une autre, est transparente sinon, elle possède une couleur définie par le navigateur.

9-2 - Le modèle des boite s CSS

Le modèle des boites CSS décrit des boites rectangulaires qui sont générées pour les éléments de l'arborescence du document et présentées selon le modèle de formatage visuel.

Chaque boite contient une zone de contenu (par exemple, du texte, une image, etc.) et en option une zone de marge interne (padding), de bordure et de marge externe (margin).

Figure 18 : modèle de boite CSS

Chaque nœud génère entre 0 et n de ces boites.

Tous les éléments ont une propriété display qui détermine le type de boite qui sera généré. Exemples :

1. block - generates a block box.

2. inline - generates one or more inline boxes.

3. none - no box is generated.

La valeur par défaut est inline, mais la feuille de style navigateur peut en configurer d'autres. Par exemple, l'affichage par défaut pour l'élément div est le bloc.

Vous pouvez trouver un exemple de feuille de style par défaut ici : www.w3.org/TR/CSS2/sample.html.

9-3 - Le schéma de positionnement

Il y a trois cas :

1. Normal : l'objet est positionné en fonction de son emplacement dans le document, ce qui signifie que sa place dans l'arbre de rendu est comme spécifié dans l'arbre DOM en fonction de son type de boite et de ses dimensions ;

2. Flottant : l'objet est d'abord placé comme pour un objet normal, puis déplacé aussi loin vers la gauche ou la droite que possible ;

3. Absolu : l'objet est placé dans l'arborescence de rendu différemment de sa place dans l'arbre DOM.

Le schéma de positionnement est défini par la propriété position et l'attribut float.

- static et relative provoquent un positionnement normal ;

- absolute et fixed provoquent un positionnement absolu.

Avec un positionnement static, aucune position n'est définie et le positionnement par défaut est utilisé. Pour les autres cas, l'auteur précise la position, haut, bas, gauche, droite.

La façon dont une boite est dessinée est déterminée par :

- le type de boite ;

- les dimensions de la boite ;

- le schéma de positionnement ;

- des informations externes comme la taille des images et la taille de l'écran.

9-4 - Les types de boites

Boite block : forme un bloc, elle a son propre rectangle sur la fenêtre du navigateur.

Figure 19 : boite bloc

Boite inline : elle ne dispose pas de son propre bloc, mais l'intérieur est un bloc conteneur.

Figure 20 : boite inline

Les boites block sont mises en forme verticalement l'une après l'autre. Les boites inline sont mises en forme à l'horizontale.

Figure 21 : le format bloc et inline

Les boites inline sont mises en ligne horizontalement, ou en «boites en ligne». Les lignes sont au moins aussi grandes que la plus grande boite, mais peut-être plus grandes, lorsque les boites sont alignées baseline, ce qui signifie que la partie inférieure d'un élément est alignée avec un point d'un autre bloc puis en bas. Dans le cas où la largeur du conteneur n'est pas suffisante, les boites inline seront mises sur plusieurs lignes. C'est généralement ce qui se passe dans un paragraphe.

Figure 22 : lignes

9-5 - Le positionnement

9-5-1 - Relatif

Le positionnement relatif : il est positionné comme d'habitude puis déplacé de la valeur requise.

Figure 23 : positionnement relatif

9-5-2 - Flottant

Une boite flottante est déplacée vers la gauche ou vers la droite d'une ligne. La caractéristique à noter est que les autres boites vont s'agencer autour d'elle. Le code HTML :

1. <p>

2. <img style="float:right" src="images/image.gif" width="100" height="100">

3. Lorem ipsum dolor sit amet, consectetuer...

4. </p>

Ressemblerait à :

Figure 24 : flottant

9-5-3 - Absolu et fixe

La mise en page est définie exactement, quel que soit le flux normal. L'élément ne participe pas au flux normal. Les dimensions sont par rapport au conteneur. Avec fixe, le conteneur est la vue du navigateur.

Figure 25 : positionnement fixe

Notez, une boite fixe ne bougera pas, même quand on fait défiler le document !

9-6 - La représentation en couche

Elle est spécifiée par la propriété CSS z-index. Elle représente la troisième dimension de la boite le long de « l'axe z ».

Les boites sont divisées en piles (appelé contexte d'empilement). Dans chaque pile, les éléments arrière seront dessinés en premier et les éléments du premier plan, plus près de l'utilisateur, ensuite. En cas de chevauchement l'élément de premier plan permet de masquer l'élément en arrière-plan.

Les piles sont classées en fonction de la propriété z-index. Les boites avec une propriété z-index forment une pile locale. La fenêtre d'affichage présente la pile externe.

Exemple :

1. <style type="text/css">

2. div {

3. position: absolute;

4. left: 2in;

5. top: 2in;

6. }

7. </style>

8. <p>

9. <div

10. style="z-index: 3;background-color:red; width: 1in; height: 1in; ">

11. </div>

12. <div

13. style="z-index: 1;background-color:green;width: 2in; height: 2in;">

14. </div>

15. </p>

Le résultat sera cela :

Figure 26 : positionnement fixe

Bien que la balise div en rouge précède la balise en vert, et aurait donc été peinte avant dans le flux régulier, sa propriété z-index est élevée, elle est donc plus en avant que la pile détenue de la boite de racine.

10 - Ressources

1. Architecture des navigateurs

1. Grosskurth, Alan. A Reference Architecture for Web Browsers (pdf) ;

2. Gupta, Vineet. How Browsers Work - Part 1 - Architecture.

2. L'analyse

1. Aho, Sethi, Ullman, Compilers: Principles, Techniques, and Tools (aka the "Dragon book"), Addison-Wesley, 1986 ;

2. Rick Jelliffe. The Bold and the Beautiful: two new drafts for HTML 5.

3. Firefox

1. L. David Baron, Faster HTML and CSS: Layout Engine Internals for Web Developers ;

2. L. David Baron, Faster HTML and CSS: Layout Engine Internals for Web Developers (Google tech talk video) ;

3. L. David Baron, Mozilla's Layout Engine ;

4. L. David Baron, Mozilla Style System Documentation ;

5. Chris Waterson, Notes on HTML Reflow ;

6. Chris Waterson, Gecko Overview ;

7. Alexander Larsson, The life of an HTML HTTP request.

4. Webkit

1. David Hyatt, Implementing CSS(part 1) ;

2. David Hyatt, An Overview of WebCore ;

3. David Hyatt, WebCore Rendering ;

4. David Hyatt, The FOUC Problem.

5. Spécifications W3C

1. HTML 4.01 Specification ;

2. W3C HTML5 Specification ;

3. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification.

6. Instruction de compilation des navigateurs

1. Firefox. https://developer.mozilla.org/en/Build_Documentation ;

2. Webkit. http://webkit.org/building/build.html.

Tali Garsiel est développeuse en Israël. Elle a commencé comme développeuse Web en 2000 et est devenue experte avec le modèle en couche du « diable » Netscape. Tout comme Richard Feynmann, elle est fascinée pour comprendre comment les choses marchent et ainsi, elle a commencé à creuser dans le fonctionnement interne des navigateurs et à documenter ce qu'elle a trouvé. Tali a aussi publié un guide rapide au sujet des performances côté client.

10-1 - Traductions

Cet article a été traduit en japonais, deux fois !

- How Browsers Work - Behind the Scenes of Modern Web Browsers (ja) par @_kosei ;

- ブラウザってどうやって動いてるの？（モダンWEBブラウザシーンの裏側 par @ikeike443 et @kiyoto01.

Merci à tout le monde !

10-2 - Remerciements Developpez

Cet article est la traduction de l'article How Browsers Work: Behind the scenes of modern web browsers. Après votre lecture, Commentez .

L'équipe de la Rédaction Developpez.com remercie toutes les personnes qui ont participé à cette traduction et à sa correction orthographique, avec par ordre alphabétique : Bovino, ClaudeLELOUP, djibril, dourouc05, FirePrawn, ram-0000 et zoom61.