Fonctionnement des navigateurs

Dans les coulisses des navigateurs Web modernes

Préface

Cette introduction complète sur les opérations internes de WebKit et Gecko est le résultat de nombreuses recherches effectuées par le développeur israélien Tali Garsiel. Plusieurs toutes les données publiées sur les composants internes des navigateurs, elle a passé beaucoup de temps à lire le code source du navigateur web. Elle a écrit:

En tant que développeur Web, apprendre les rouages des opérations des navigateurs vous aide à prendre de meilleures décisions et à connaître les raisons du développement bonnes pratiques. Ce document est assez long, mais nous vous recommandons vous passez du temps à y creuser. Vous ne le regretterez pas.

Paul Irish, Chrome Developer Relations

Introduction

Les navigateurs Web sont les logiciels les plus utilisés. Dans cette introduction, j’explique comment ils travaillent en coulisse. Nous verrons ce qui se passe lorsque vous saisissez google.com. dans la barre d'adresse jusqu'à ce que la page Google s'affiche sur l'écran du navigateur.

Les navigateurs dont nous parlerons

Cinq principaux navigateurs sont actuellement utilisés sur les ordinateurs de bureau: Chrome, Internet Explorer, Firefox, Safari et Opera. Sur les mobiles, les principaux navigateurs sont le navigateur Android, l'iPhone, Opera Mini et Opera Mobile, le navigateur UC, les navigateurs Nokia S40/S60 et Chrome, qui sont tous basés sur WebKit, à l'exception des navigateurs Opera. Je vais vous donner des exemples tirés des navigateurs Open Source Firefox, Chrome et Safari (qui est en partie Open Source). Selon les statistiques StatCounter (données de juin 2013), Chrome, Firefox et Safari représentent environ 71% de l'utilisation des navigateurs pour ordinateur dans le monde. Sur les mobiles, le navigateur Android, l'iPhone et Chrome constituent environ 54% de l'utilisation.

Fonctionnalité principale du navigateur

La fonction principale d'un navigateur consiste à présenter la ressource Web de son choix, en la demandant au serveur et en l'affichant dans la fenêtre du navigateur. Il s'agit généralement d'un document HTML, mais il peut également s'agir d'un PDF, d'une image ou d'un autre type de contenu. L'emplacement de la ressource est spécifié par l'utilisateur à l'aide d'un URI (Uniform Resource Identifier).

La manière dont le navigateur interprète et affiche les fichiers HTML est spécifiée dans les spécifications HTML et CSS. Ces spécifications sont gérées par l'organisation W3C (World Wide Web Consortium), qui est l'organisme de normalisation pour le Web. Pendant des années, les navigateurs n'étaient conformes qu'à une partie des spécifications et développaient leurs propres extensions. Cela entraînait de graves problèmes de compatibilité pour les auteurs Web. Aujourd'hui, la plupart des navigateurs sont plus ou moins conformes aux spécifications.

Les interfaces utilisateur des navigateurs ont beaucoup de points communs entre elles. Voici quelques-uns des éléments d'interface utilisateur courants:

  1. Barre d'adresse pour insérer un URI
  2. Boutons "Précédent" et "Suivant"
  3. Options de favoris
  4. Boutons "Actualiser" et "Arrêter" pour actualiser ou arrêter le chargement des documents actuels
  5. Bouton d'accueil qui vous redirige vers la page d'accueil

Étrangement, l'interface utilisateur du navigateur n'est spécifiée dans aucune spécification formelle. Elle provient simplement de bonnes pratiques qui se sont développées au fil des années d'expérience et des navigateurs qui s'imitent les uns les autres. La spécification HTML5 ne définit pas les éléments d'interface utilisateur requis par un navigateur, mais énumère quelques éléments communs. comme la barre d'adresse, la barre d'état et la barre d'outils. Il existe bien sûr des fonctionnalités propres à chaque navigateur, comme le gestionnaire de téléchargements de Firefox.

Infrastructure de haut niveau

Les principaux composants du navigateur sont les suivants:

  1. L'interface utilisateur: elle comprend la barre d'adresse, le bouton "Précédent/Suivant", le menu d'ajout aux favoris, etc. Chaque partie du navigateur s'affiche, à l'exception de la fenêtre dans laquelle vous voyez la page demandée.
  2. Le moteur du navigateur regroupe les actions entre l'interface utilisateur et le moteur de rendu.
  3. Moteur de rendu: chargé d'afficher le contenu demandé. Par exemple, si le contenu demandé est au format HTML, le moteur de rendu analyse le code HTML et CSS, puis affiche le contenu analysé à l'écran.
  4. Mise en réseau: pour les appels réseau tels que les requêtes HTTP, utilisant différentes implémentations pour différentes plates-formes derrière une interface indépendante de la plate-forme.
  5. Backend de l'interface utilisateur: utilisé pour dessiner des widgets de base tels que les boîtes combinées et les fenêtres. Ce backend expose une interface générique qui n'est pas spécifique à la plate-forme. En dessous, il utilise des méthodes d'interface utilisateur du système d'exploitation.
  6. Interpréteur JavaScript. Utilisé pour analyser et exécuter le code JavaScript.
  7. Stockage de données. Il s'agit d'une couche de persistance. Le navigateur peut avoir besoin d'enregistrer toutes sortes de données localement, telles que des cookies. Les navigateurs sont également compatibles avec des mécanismes de stockage tels que localStorage, IndexedDB, WebSQL et FileSystem.
Composants du navigateur
Figure 1: Composants du navigateur

Il est important de noter que les navigateurs comme Chrome exécutent plusieurs instances du moteur de rendu: une pour chaque onglet. Chaque onglet s'exécute dans un processus distinct.

Moteurs de rendu

La responsabilité du moteur de rendu est la suivante : le rendu, c'est-à-dire l'affichage du contenu demandé sur l'écran du navigateur.

Par défaut, le moteur de rendu peut afficher des images et des documents HTML et XML. Il peut afficher d'autres types de données via des plug-ins ou des extensions. par exemple, l'affichage de documents PDF à l'aide d'un plug-in de visionneuse de PDF. Toutefois, dans ce chapitre, nous nous concentrerons sur le principal cas d'utilisation: afficher du code HTML et des images au format CSS.

Les moteurs de rendu diffèrent selon les navigateurs: Internet Explorer utilise Trident, Firefox utilise Gecko, Safari utilise WebKit. Chrome et Opera (à partir de la version 15) utilisent Blink, un fork de WebKit.

WebKit est un moteur de rendu Open Source. Initialement utilisé comme moteur pour la plate-forme Linux, il a été modifié par Apple pour être compatible avec Mac et Windows.

Flux principal

Le moteur de rendu commencera à récupérer le contenu du document demandé de la couche réseau. Cela se fait généralement par blocs de 8 Ko.

Voici le flux de base du moteur de rendu:

Flux de base du moteur de rendu
Figure 2: Flux de base du moteur de rendu

Le moteur de rendu commence à analyser le document HTML et convertit les éléments en nœuds DOM dans une arborescence appelée "arborescence de contenu". Le moteur analyse les données de style, à la fois dans les fichiers CSS externes et dans les éléments de style. Les informations de style et les instructions visuelles du code HTML permettront de créer une autre arborescence: l'arborescence de rendu.

L'arborescence de rendu contient des rectangles avec des attributs visuels tels que la couleur et les dimensions. Les rectangles sont dans le bon ordre pour s'afficher à l'écran.

Après la construction de l'arborescence de rendu, une mise en page est appliquée. processus. En d'autres termes, vous devez attribuer à chaque nœud les coordonnées exactes où il doit apparaître à l'écran. L'étape suivante est la peinture : l'arborescence de rendu est balayée, et chaque nœud est peint à l'aide du calque backend de l'interface utilisateur.

Il est important de comprendre qu'il s'agit d'un processus progressif. Pour améliorer l'expérience utilisateur, le moteur de rendu tente d'afficher le contenu à l'écran dès que possible. Il n'attend pas que tout le code HTML soit analysé pour commencer à créer et à mettre en page l'arborescence de rendu. Une partie du contenu est analysée et affichée, tandis que le processus se poursuit avec le reste du contenu qui continue de provenir du réseau.

Exemples de flux principal

Flux principal de WebKit.
Figure 3: Flux principal de WebKit
Flux principal du moteur de rendu Gecko de Mozilla
Figure 4: Flux principal du moteur de rendu Gecko de Mozilla

Les figures 3 et 4 montrent que, bien que WebKit et Gecko utilisent une terminologie légèrement différente, le flux est essentiellement le même.

Gecko appelle l'arborescence des éléments mis en forme visuellement comme une "arborescence de frames". Chaque élément est un cadre. WebKit utilise le terme "arborescence de rendu" et elle se compose de "Render Objects" (Objets de rendu). WebKit utilise le terme « mise en page » pour le placement des éléments, tandis que Gecko l'appelle "Reflow". "Pièce jointe" est le terme utilisé par WebKit pour connecter les nœuds DOM et les informations visuelles afin de créer l'arborescence de rendu. Une différence non sémantique mineure est que Gecko dispose d'une couche supplémentaire entre l'arborescence HTML et DOM. Il s'agit du "récepteur de contenu" et sert à fabriquer des éléments DOM. Nous aborderons chaque partie du parcours:

Analyse – général

Étant donné que l'analyse est un processus très important dans le moteur de rendu, nous allons l'examiner un peu plus en détail. Commençons par une petite introduction à l'analyse.

Analyser un document consiste à le traduire dans une structure que le code peut utiliser. Le résultat de l'analyse est généralement une arborescence de nœuds qui représente la structure du document. C'est ce qu'on appelle un arbre d'analyse ou une arborescence syntaxique.

Par exemple, l'analyse de l'expression 2 + 3 - 1 peut renvoyer l'arborescence suivante:

Nœud d'arbre d'expression mathématique.
Figure 5: Nœud d'arbre d'expression mathématique

Grammaire

L'analyse est basée sur les règles de syntaxe du document: la langue ou le format dans lequel il a été rédigé. Chaque format que vous pouvez analyser doit avoir une grammaire déterministe composée de vocabulaire et de règles de syntaxe. C'est ce qu'on appelle un grammaire sans contexte. Les langues humaines ne sont pas des langages de ce type et ne peuvent donc pas être analysées à l'aide de techniques d'analyse conventionnelles.

Analyseur-combinaison lexer

L'analyse peut être divisée en deux sous-processus: l'analyse lexicale et l'analyse syntaxique.

L'analyse lexicale est le processus qui consiste à décomposer l'entrée en jetons. Les jetons représentent le vocabulaire du langage: l'ensemble des éléments de base valides. En langage humain, il s'agira de tous les mots qui apparaissent dans le dictionnaire propre à cette langue.

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

Les analyseurs divisent généralement le travail entre deux composants: le lexer (parfois appelé tokenizer) est chargé de décomposer l'entrée en jetons valides, et l'analyseur qui est chargé de construire l'arborescence d'analyse en analysant la structure du document selon les règles de syntaxe du langage.

Le lexer sait comment supprimer les caractères non pertinents, comme les espaces et les sauts de ligne.

Du document source à l'analyse des arborescences
Figure 6: Du document source à l'analyse des arborescences

Le processus d'analyse est itératif. L'analyseur demande généralement un nouveau jeton au lexer et essaie de le faire correspondre à l'une des règles de syntaxe. Si une règle correspond, un nœud correspondant au jeton est ajouté à l'arborescence d'analyse et l'analyseur demande un autre jeton.

Si aucune règle ne correspond, l'analyseur stocke le jeton en interne et continue de demander des jetons jusqu'à ce qu'une règle correspondant à tous les jetons stockés en interne soit trouvée. Si aucune règle n'est trouvée, l'analyseur génère une exception. Cela signifie que le document n'était pas valide et qu'il contenait des erreurs de syntaxe.

Traduction

Dans de nombreux cas, l'arbre syntaxique n'est pas le produit final. L'analyse est souvent utilisée en traduction, c'est-à-dire pour transformer le document d'entrée dans un autre format. La compilation en est un exemple. Le compilateur qui compile le code source en code machine l'analyse d'abord dans un arbre d'analyse, puis le traduit en un document de code machine.

Flux de compilation
Figure 7: Flux de compilation

Exemple d'analyse

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

Syntaxe :

  1. Les éléments de base de la syntaxe du langage sont les expressions, les termes et les opérations.
  2. Notre langage peut inclure un nombre illimité d'expressions.
  3. Une expression est définie comme un "terme" suivie d'une "opération" suivi d'un autre terme
  4. Une opération est un jeton plus ou moins
  5. Un terme est un jeton d'entier ou une expression

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

La première sous-chaîne qui correspond à une règle est 2: selon la règle n°5, il s'agit d'un terme. La deuxième correspondance est 2 + 3 : elle correspond à la troisième règle, à savoir un terme suivi d'une opération suivie d'un autre terme. La correspondance suivante ne sera appelée qu'à la fin de l'entrée. 2 + 3 - 1 est une expression, car nous savons déjà que 2 + 3 est un terme. Nous avons donc un terme suivi d'une opération suivie d'un autre terme. 2 + + ne correspond à aucune règle et n'est donc pas une entrée valide.

Définitions formelles du vocabulaire et de la syntaxe

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

Par exemple, notre langue sera définie comme suit:

INTEGER: 0|[1-9][0-9]*
PLUS: +
MINUS: -

Comme vous pouvez le voir, les entiers sont définis par une expression régulière.

La syntaxe est généralement définie dans un format appelé BNF. Notre langue sera définie comme suit:

expression :=  term  operation  term
operation :=  PLUS | MINUS
term := INTEGER | expression

Nous avons vu qu'une langue peut être analysée par des analyseurs standards si sa grammaire est sans contexte. La définition intuitive d'une grammaire sans contexte est une grammaire qui peut être entièrement exprimée en BNF. Pour une définition formelle, consultez Article Wikipédia sur la grammaire sans contexte

Types d'analyseurs

Il existe deux types d'analyseurs: les analyseurs de haut en bas et ceux de bas en haut. Une explication intuitive est que les analyseurs descendants examinent la structure de haut niveau de la syntaxe et tentent de trouver une correspondance de règle. Les analyseurs ascendants commencent avec l'entrée et la transforment progressivement en règles de syntaxe, en commençant par les règles de niveau inférieur jusqu'à ce que les règles de niveau supérieur soient remplies.

Voyons comment les deux types d'analyseurs analysent notre exemple.

L'analyseur de haut en bas commence par la règle de niveau supérieur: il identifie 2 + 3 en tant qu'expression. Il identifie ensuite 2 + 3 - 1 en tant qu'expression (le processus d'identification de l'expression évolue pour correspondre aux autres règles, mais le point de départ est la règle de niveau le plus élevé).

L'analyseur de bas en haut analyse l'entrée jusqu'à ce qu'une règle corresponde. Elle remplacera ensuite l'entrée correspondante par la règle. Cela se poursuit jusqu'à la fin de l'entrée. L'expression partiellement correspondante est placée dans la pile de l'analyseur.

Empiler Entrée
2 + 3 - 1
terme + 3 - 1
opération sur un terme 3 – 1
expression – 1
opération d'expression 1
expression -

Ce type d'analyseur de bas en haut est appelé "analyseur Maj/Réduire", car l'entrée est décalée vers la droite (imaginez un pointeur pointant d'abord vers le début de l'entrée et se déplaçant vers la droite) et progressivement réduite aux règles de syntaxe.

Générer des analyseurs automatiquement

Il existe des outils permettant de générer un analyseur. Vous leur fournissez la grammaire de votre langue, son vocabulaire et ses règles de syntaxe, et ils génèrent un analyseur fonctionnel. La création d'un analyseur nécessite une connaissance approfondie de l'analyse. De plus, il n'est pas facile de créer manuellement un analyseur optimisé. C'est pourquoi les générateurs d'analyseur peuvent être très utiles.

WebKit utilise deux générateurs d'analyseur bien connus: Flex pour créer un lexer et Bison pour créer un analyseur (vous pourriez les rencontrer sous les noms Lex et Yacc). L'entrée Flex est un fichier contenant les définitions des expressions régulières des jetons. L'entrée de Bison correspond aux règles de syntaxe du langage au format BNF.

Analyseur HTML

Le rôle de l'analyseur HTML est d'analyser le balisage HTML dans une arborescence d'analyse.

Grammaire HTML

Le vocabulaire et la syntaxe du langage HTML sont définis dans des spécifications créées par l'organisme W3C.

Comme nous l'avons vu dans l'introduction de l'analyse, la syntaxe grammaticale peut être définie formellement à l'aide de formats tels que BNF.

Malheureusement, tous les sujets relatifs aux analyseurs classiques ne s'appliquent pas au code HTML. Je ne les ai pas mentionnés pour le plaisir. Ils serviront à analyser les fichiers CSS et JavaScript. Le code HTML ne peut pas être facilement défini par une grammaire sans contexte dont les analyseurs ont besoin.

Il existe un format formel pour définir le HTML, la DTD (Document Type Definition), mais il ne s'agit pas d'une grammaire sans contexte.

Cela semble étrange à première vue ; HTML est plutôt proche du XML. De nombreux analyseurs XML sont disponibles. Il existe une variante XML du langage HTML (XHTML). Quelle est la différence majeure ?

La différence réside dans le fait que l'approche HTML est plus "indulgente" : elle vous permet d'omettre certaines balises (qui sont ensuite ajoutées implicitement), ou d'omettre parfois des balises de début ou de fin, etc. Globalement, c'est une sorte de par opposition à la syntaxe stricte et exigeante du XML.

Ce petit détail en apparence fait toute la différence. D'une part, c'est la raison principale pour laquelle le langage HTML est si populaire: il pardonne vos erreurs et facilite la vie de l'auteur du Web. D'autre part, il est difficile de rédiger une grammaire formelle. En résumé, les analyseurs conventionnels ne peuvent pas analyser facilement le code HTML, car sa grammaire n'est pas libre en contexte. Le code HTML ne peut pas être analysé par les analyseurs XML.

DTD HTML

La définition HTML est au format DTD. Ce format est utilisé pour définir les langages de la famille SGML. Le format contient les définitions de tous les éléments autorisés, de leurs attributs et de 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 la DTD. Le mode strict respecte uniquement les spécifications, mais d'autres modes prennent en charge le balisage utilisé par les navigateurs auparavant. L'objectif est d'assurer la rétrocompatibilité avec les contenus plus anciens. La DTD stricte actuelle est disponible ici: www.w3.org/TR/html4/strict.dtd

DOM

L'arborescence de sortie ("l'arborescence d'analyse") est une arborescence de nœuds d'éléments DOM et d'attributs. DOM est l'abréviation de Document Object Model. Il s'agit de la présentation d'objet du document HTML et de l'interface des éléments HTML vers le monde extérieur, comme JavaScript.

La racine de l'arborescence est la colonne Document. .

Le DOM a une relation de type un à un par rapport au balisage. Exemple :

<html>
  <body>
    <p>
      Hello World
    </p>
    <div> <img src="example.png"/></div>
  </body>
</html>

Ce balisage serait traduit dans l'arborescence DOM suivante:

Arborescence DOM de l&#39;exemple de balisage
Figure 8: Arborescence DOM de l'exemple de balisage

Comme pour HTML, le DOM est spécifié par l'organisation W3C. Consultez la page www.w3.org/DOM/DOMTR. Il s'agit d'une spécification générique permettant de manipuler des documents. Un module spécifique décrit des éléments HTML spécifiques. Les définitions HTML sont disponibles ici: www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.html.

Lorsque je dis que l'arborescence contient des nœuds DOM, cela signifie qu'elle est construite d'éléments qui implémentent l'une des interfaces DOM. Les navigateurs utilisent des implémentations concrètes avec d'autres attributs utilisés en interne par le navigateur.

L'algorithme d'analyse

Comme nous l'avons vu dans les sections précédentes, il n'est pas possible d'analyser le code HTML à l'aide des analyseurs classiques de haut en bas ou de bas en haut.

Pour les raisons suivantes :

  1. La nature pardonnante du langage.
  2. Les navigateurs ont une tolérance aux erreurs traditionnelle pour prendre en charge les cas connus de code HTML non valide.
  3. Le processus d'analyse est réentrant. Pour les autres langages, la source ne change pas lors de l'analyse. En revanche, en HTML, le code dynamique (comme les éléments de script contenant des appels document.write()) peut ajouter des jetons supplémentaires. Le processus d'analyse modifie donc l'entrée.

Impossible d'utiliser les techniques d'analyse standards. Les navigateurs créent des analyseurs personnalisés pour analyser le code HTML.

L'algorithme d'analyse est décrit en détail dans la spécification HTML5. L'algorithme comprend deux étapes: la tokenisation et la construction d'arborescence.

La tokenisation est l'analyse lexicale qui consiste à convertir les entrées en jetons. Parmi les jetons HTML, citons les balises de début, les balises de fin, les noms d'attributs et les valeurs d'attribut.

La fonction de tokenisation reconnaît le jeton, le transmet au constructeur de l'arborescence et utilise le caractère suivant pour reconnaître le jeton suivant, et ainsi de suite jusqu'à la fin de l'entrée.

Flux d&#39;analyse HTML (issu de la spécification HTML5)
Figure 9: Flux d'analyse HTML (issu de la spécification HTML5)

L'algorithme de tokenisation

Le résultat de l'algorithme est un jeton HTML. L'algorithme est exprimé sous la forme d'une machine à états. Chaque état utilise 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 actuel de tokenisation et par l'état de construction de l'arborescence. Cela signifie que le même caractère consommé produira des résultats différents pour le bon état suivant, en fonction de l'état actuel. Cet algorithme étant trop complexe pour être décrit en détail, prenons un exemple simple pour mieux comprendre le principe.

Exemple de base – tokenisation du code HTML suivant:

<html>
  <body>
    Hello world
  </body>
</html>

L'état initial est "État des données". Lorsque le caractère < est rencontré, l'état passe à "État d'ouverture du tag". L'utilisation d'un caractère a-z entraîne la création d'un "jeton de balise de début". L'état passe alors à État du nom du tag. Nous conservons cet état jusqu'à ce que le caractère > soit utilisé. Chaque caractère est ajouté au nom du nouveau jeton. Dans notre cas, le jeton créé est un jeton html.

Lorsque la balise > est atteinte, le jeton actuel est émis et l'état redevient "Data state" (État des données). La balise <body> sera traitée de la même manière. Jusqu'à présent, les balises html et body ont été émises. Nous sommes maintenant de retour à l'état des données. L'utilisation du caractère H de Hello world entraîne la création et l'émission d'un jeton de caractère, qui se poursuit jusqu'à ce que la < de </body> soit atteinte. Nous allons émettre un jeton de caractère pour chaque caractère de Hello world.

L'état d'ouverture du tag est de retour. L'utilisation de la prochaine entrée / entraînera la création d'un end tag token et le passage à l'état du nom de la balise. Nous conservons à nouveau cet état jusqu'à ce que nous atteignions >.Le nouveau jeton de tag est alors émis, et nous revenons à l'état des données. L'entrée </html> sera traitée comme le cas précédent.

Tokeniser l&#39;exemple d&#39;entrée
Figure 10: Tokeniser l'exemple d'entrée

Algorithme de construction d'arbre

L'objet Document est généré lors de la création de l'analyseur. Lors de la phase de construction de l'arborescence, l'arborescence DOM dont la racine contient le document est modifiée et des éléments y sont ajoutés. Chaque nœud émis par la fonction de tokenisation sera traité par le constructeur d'arborescence. Pour chaque jeton, la spécification définit l'élément DOM correspondant et sera créé pour ce jeton. L'élément est ajouté à l'arborescence DOM, ainsi qu'à la pile d'éléments ouverts. Cette pile est utilisée pour corriger les incohérences d'imbrication et les tags non fermés. L'algorithme est également décrit comme une machine à états. Les états sont appelés "modes d'insertion".

Examinons le processus de construction d'une arborescence pour l'exemple d'entrée:

<html>
  <body>
    Hello world
  </body>
</html>

L'entrée de l'étape de construction de l'arborescence est une séquence de jetons issus de l'étape de tokenisation. Le premier mode est le mode initial. Recevoir le code "html" Le jeton est alors déplacé en mode "before html", puis traité à nouveau dans ce mode. L'élément HTMLHTMLElement, qui sera ajouté à l'objet racine du document sera alors créé.

L'état sera remplacé par "before head". Le "corps" le jeton est ensuite reçu. Un élément HTMLHeadElement est créé implicitement, même si nous ne disposons pas d'un élément "head". qui sera ajouté à l'arborescence.

Passons maintenant au mode in head (en tête), puis à after head (après). Le jeton "body" est à nouveau traité, un élément HTMLBodyElement est créé et inséré, et le mode est transféré vers "in body".

Les jetons de caractère de "Hello World" sont maintenant reçues. Le premier entraînera la création et l'insertion d'un texte et les autres caractères seront ajoutés à ce nœud.

La réception du jeton de fin du corps entraîne le transfert en mode "after body". Nous allons maintenant recevoir la balise de fin HTML, ce qui nous déplacera vers le mode "after after body". La réception du jeton de fin de fichier mettra fin à l'analyse.

Construction d&#39;arborescence d&#39;exemple de code HTML.
Figure 11: arborescence de l'exemple HTML

Actions à la fin de l'analyse

À ce stade, le navigateur marque le document comme interactif et commence à analyser les scripts dont l'état est "différé". mode: ceux qui doivent être exécutés après l'analyse du document. L'état du document est alors défini sur "complete" (terminé). et un « chargement » sera déclenché.

Vous pouvez consulter les algorithmes complets de tokenisation et de construction d'arborescence dans la spécification HTML5.

Navigateurs tolérance aux erreurs

Vous n'obtiendrez jamais de « syntaxe non valide » sur une page HTML. Les navigateurs corrigent les problèmes de contenu non valide et poursuivent le processus.

Prenons l'exemple de ce code HTML:

<html>
  <mytag>
  </mytag>
  <div>
  <p>
  </div>
    Really lousy HTML
  </p>
</html>

J'ai dû enfreindre un million de règles ("mytag" n'est pas un tag standard, imbrication incorrecte des éléments "p" et "div", etc.), mais le navigateur l'affiche toujours correctement et ne se plaint pas. Une grande partie du code de l'analyseur corrige les erreurs liées à l'auteur HTML.

La gestion des erreurs est assez cohérente dans les navigateurs, mais il est étonnant qu'elle ne fasse pas partie des spécifications HTML. Tout comme les favoris et les boutons "Précédent" et "Suivant", ce sont des fonctionnalités qui ont été développées dans les navigateurs au fil des années. Des constructions HTML non valides se répètent sur de nombreux sites. Les navigateurs essaient de les corriger conformément aux autres navigateurs.

La spécification HTML5 définit certaines de ces exigences. (WebKit résume parfaitement ce point dans le commentaire placé au début de la classe d'analyseur HTML.)

L'analyseur analyse l'entrée tokenisée dans le document, créant ainsi l'arborescence de documents. Si le document est bien formé, son analyse est simple.

Malheureusement, nous devons traiter de nombreux documents HTML dont le format n'est pas correct. L'analyseur doit donc tolérer les erreurs.

Nous devons au moins gérer les conditions d'erreur suivantes:

  1. L'élément ajouté est explicitement interdit dans une balise externe. Dans ce cas, nous devons fermer toutes les balises jusqu'à celle qui interdit l'élément, puis l'ajouter par la suite.
  2. Nous ne sommes pas autorisés à ajouter l'élément directement. Il se peut que la personne qui écrit le document ait oublié une balise entre les deux (ou que celle-ci soit facultative). Cela peut être le cas avec les balises suivantes: HTML HEAD BODY TBODY TR TD LI (en ai-je oublié quelques ?).
  3. Nous voulons ajouter un élément de type bloc à l'intérieur d'un élément intégré. Fermez tous les éléments intégrés jusqu'à l'élément de bloc suivant situé plus haut.
  4. Si le problème persiste, fermez les éléments jusqu'à ce que nous soyons autorisés à les ajouter ou ignorez la balise.

Voyons quelques exemples de tolérance aux erreurs de WebKit:

</br> au lieu de <br>

Certains sites utilisent </br> au lieu de <br>. Pour être compatible avec IE et Firefox, WebKit considère cela comme <br>.

Le code:

if (t->isCloseTag(brTag) && m_document->inCompatMode()) {
     reportError(MalformedBRError);
     t->beginTag = true;
}

Notez que la gestion des erreurs est interne et ne sera pas présentée à l'utilisateur.

Un tableau perdu

Il s'agit d'un tableau situé à l'intérieur d'un autre tableau, mais pas à l'intérieur d'une cellule.

Exemple :

<table>
  <table>
    <tr><td>inner table</td></tr>
  </table>
  <tr><td>outer table</td></tr>
</table>

WebKit modifie la hiérarchie en deux tables sœurs:

<table>
  <tr><td>outer table</td></tr>
</table>
<table>
  <tr><td>inner table</td></tr>
</table>

Le code:

if (m_inStrayTableContent && localName == tableTag)
        popBlock(tableTag);

WebKit utilise une pile pour le contenu de l'élément actuel: il fera sortir la table interne de la pile de table externe. Les tables seront désormais frères.

Éléments de formulaire imbriqués

Si l'utilisateur place un formulaire dans un autre, le deuxième formulaire est ignoré.

Le code:

if (!m_currentFormElement) {
        m_currentFormElement = new HTMLFormElement(formTag,    m_document);
}

Hiérarchie de balises trop profonde

Le commentaire parle de lui-même.

bool HTMLParser::allowNestedRedundantTag(const AtomicString& tagName)
{

unsigned i = 0;
for (HTMLStackElem* curr = m_blockStack;
         i < cMaxRedundantTagDepth && curr && curr->tagName == tagName;
     curr = curr->next, i++) { }
return i != cMaxRedundantTagDepth;
}

Balises de fin HTML ou de corps mal placées

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

if (t->tagName == htmlTag || t->tagName == bodyTag )
        return;

Les auteurs Web doivent donc être vigilants : à moins que vous ne souhaitiez prendre l'exemple d'un extrait de code de tolérance des erreurs WebKit, écrivez un code HTML bien formé.

Analyse CSS

Vous vous souvenez des concepts d'analyse présentés dans l'introduction ? Contrairement au langage HTML, le CSS est une grammaire sans contexte qui peut être analysée à l'aide des types d'analyseurs décrits dans l'introduction. En fait, la spécification CSS définit la grammaire lexicale et syntaxique du CSS.

Voyons quelques exemples:

La grammaire lexicale (vocabulaire) est définie par des expressions régulières pour chaque jeton:

comment   \/\*[^*]*\*+([^/*][^*]*\*+)*\/
num       [0-9]+|[0-9]*"."[0-9]+
nonascii  [\200-\377]
nmstart   [_a-z]|{nonascii}|{escape}
nmchar    [_a-z0-9-]|{nonascii}|{escape}
name      {nmchar}+
ident     {nmstart}{nmchar}*

&quot;ident&quot; est l'abréviation d'identifiant, comme un nom de classe. "nom" est un identifiant d'élément (appelé "#" ) ;

La grammaire de la syntaxe est décrite dans BNF.

ruleset
  : selector [ ',' S* selector ]*
    '{' S* declaration [ ';' S* declaration ]* '}' S*
  ;
selector
  : simple_selector [ combinator selector | S+ [ combinator? selector ]? ]?
  ;
simple_selector
  : element_name [ HASH | class | attrib | pseudo ]*
  | [ HASH | class | attrib | pseudo ]+
  ;
class
  : '.' IDENT
  ;
element_name
  : IDENT | '*'
  ;
attrib
  : '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
    [ IDENT | STRING ] S* ] ']'
  ;
pseudo
  : ':' [ IDENT | FUNCTION S* [IDENT S*] ')' ]
  ;

Explication :

Un ensemble de règles est la structure suivante:

div.error, a.error {
  color:red;
  font-weight:bold;
}

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

ruleset
  : selector [ ',' S* selector ]*
    '{' S* declaration [ ';' S* declaration ]* '}' S*
  ;

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

Analyseur CSS WebKit

WebKit utilise des générateurs d'analyseurs Flex et Bison pour créer des analyseurs automatiquement à partir des fichiers de grammaire CSS. Comme nous l'avons vu dans l'introduction de l'analyseur, Bison crée un analyseur de bas en haut avec réduction des décalages. Firefox utilise un analyseur descendant écrit manuellement. Dans les deux cas, chaque fichier CSS est analysé dans un objet de feuille de style. Chaque objet contient des règles CSS. Les objets de règle CSS contiennent des objets sélecteur et de déclaration, ainsi que d'autres objets correspondant à la grammaire CSS.

Analyse des fichiers CSS.
Figure 12: analyse du code CSS

Ordre de traitement des scripts et des feuilles de style

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'interrompt jusqu'à l'exécution du script. Si le script est externe, la ressource doit d'abord être extraite du réseau. Cette opération s'effectue également de manière synchrone, et l'analyse s'interrompt jusqu'à ce que la ressource soit récupérée. Il s'agissait du modèle utilisé pendant de nombreuses années, et il est également spécifié dans les spécifications HTML4 et 5. Les auteurs peuvent ajouter l'option "Différer" à un script, auquel cas il n'interrompt pas l'analyse du document et s'exécute après l'analyse du document. HTML5 ajoute une option permettant de marquer le script comme asynchrone afin qu'il soit analysé et exécuté par un autre thread.

Analyse spéculative

WebKit et Firefox procèdent tous deux à cette optimisation. Lors de l'exécution des scripts, un autre thread analyse le reste du document et détermine quelles autres ressources doivent être chargées à partir du réseau, puis les charge. Ainsi, les ressources peuvent être chargées sur des connexions parallèles et la vitesse globale est améliorée. Remarque : L'analyseur spéculatif n'analyse que les références à des ressources externes telles que des scripts externes, des feuilles de style et des images. Il ne modifie pas l'arborescence DOM (qui est laissée à l'analyseur principal).

Feuilles de style

Les feuilles de style, quant à elles, ont un modèle différent. Conceptuellement, comme les feuilles de style ne modifient pas l'arborescence DOM, il n'y a aucune raison de les attendre et d'arrêter l'analyse du document. Toutefois, les scripts demandent des informations de style lors de l'analyse du document. Si le style n'est pas encore chargé et analysé, le script obtiendra des réponses incorrectes, ce qui entraînera apparemment de nombreux problèmes. Il semble s'agir d'un cas limite, mais il est assez courant. Firefox bloque tous les scripts lorsqu'une feuille de style est toujours en cours de chargement et d'analyse. WebKit bloque les scripts uniquement lorsqu'ils tentent d'accéder à certaines propriétés de style susceptibles d'être affectées par des feuilles de style déchargées.

Construction d'arborescence de rendu

Pendant la construction de l'arborescence DOM, le navigateur en construit une autre, l'arborescence de rendu. Cette arborescence contient les éléments visuels dans l'ordre dans lequel ils seront affichés. Il s'agit de la représentation visuelle du document. L'objectif de cette arborescence est de permettre de peindre le contenu dans le bon ordre.

Firefox appelle les éléments de l'arborescence de rendu "frames". WebKit utilise le terme "moteur de rendu" ou "objet de rendu".

Un moteur de rendu sait comment se mettre en page et se peindre, ainsi que ses enfants.

La classe RenderObject de WebKit, la classe de base des moteurs de rendu, présente la définition suivante:

class RenderObject{
  virtual void layout();
  virtual void paint(PaintInfo);
  virtual void rect repaintRect();
  Node* node;  //the DOM node
  RenderStyle* style;  // the computed style
  RenderLayer* containgLayer; //the containing z-index layer
}

Chaque moteur de rendu représente une zone rectangulaire qui correspond généralement à la zone CSS d'un nœud, comme décrit dans la spécification CSS2. Il comprend des informations géométriques telles que la largeur, la hauteur et la position.

Le type de cadre dépend du paramètre "display" valeur de l'attribut de style pertinente pour le nœud (voir la section Calcul de style). Voici le code WebKit permettant de choisir le type de moteur de rendu à créer pour un nœud DOM, en fonction de l'attribut display:

RenderObject* RenderObject::createObject(Node* node, RenderStyle* style)
{
    Document* doc = node->document();
    RenderArena* arena = doc->renderArena();
    ...
    RenderObject* o = 0;

    switch (style->display()) {
        case NONE:
            break;
        case INLINE:
            o = new (arena) RenderInline(node);
            break;
        case BLOCK:
            o = new (arena) RenderBlock(node);
            break;
        case INLINE_BLOCK:
            o = new (arena) RenderBlock(node);
            break;
        case LIST_ITEM:
            o = new (arena) RenderListItem(node);
            break;
       ...
    }

    return o;
}

Le type d'élément est également pris en compte. Par exemple, les commandes de formulaire et les tableaux ont des cadres spéciaux.

Dans WebKit, si un élément souhaite créer un moteur de rendu spécial, la méthode createRenderer() est ignorée. Les moteurs de rendu pointent vers des objets de style contenant des informations non géométriques.

Relation entre l'arborescence d'affichage et l'arborescence DOM

Les moteurs de rendu correspondent à des éléments DOM, mais la relation n'est pas un à un. Les éléments DOM non visuels ne seront pas insérés dans l'arborescence de rendu. Par exemple, la partie "head" . Inclut également les éléments dont la valeur d'affichage a été attribuée à "none" n'apparaissent pas dans l'arborescence (alors que les éléments avec une visibilité "masquée" apparaissent dans l'arborescence).

Certains éléments DOM correspondent à plusieurs objets visuels. Il s'agit généralement d'éléments ayant une structure complexe qui ne peut pas être décrit par un seul rectangle. Par exemple, l'option « select » comporte trois moteurs de rendu: 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 divisé en plusieurs lignes parce que la largeur n'est pas suffisante pour une ligne, les nouvelles lignes sont ajoutées en tant que moteurs de rendu supplémentaires.

Le code HTML défectueux est un autre exemple de plusieurs moteurs de rendu. Conformément à la spécification CSS, un élément intégré doit contenir soit des éléments de type bloc, soit des éléments intégrés. Dans le cas d'un contenu mixte, des moteurs de rendu de bloc anonymes sont créés pour encapsuler les éléments intégrés.

Certains objets de rendu correspondent à un nœud DOM, mais ne se trouvent pas au même endroit dans l'arborescence. Les éléments flottants et les éléments positionnés de façon absolue sont hors flux, placés dans une autre partie de l'arbre et mappés au cadre réel. Un cadre d'espace réservé est l'endroit où ils auraient dû se trouver.

Arborescence de rendu et arborescence DOM correspondante
Figure 13: Arborescence de rendu et arborescence DOM correspondante "Fenêtre d'affichage" est le bloc conteneur initial. Dans WebKit, il s'agira de "RenderView" objet

Le déroulement de la construction de l'arbre

Dans Firefox, la présentation est enregistrée en tant qu'écouteur pour les mises à jour DOM. La présentation délègue la création des frames à FrameConstructor, et le constructeur résout le style (voir Calcul de style) et crée un frame.

Dans WebKit, le processus de résolution du style et de création d'un moteur de rendu est appelé "rattachement". Chaque nœud DOM possède une fonction "attach" . Le rattachement est synchrone. L'insertion de nœuds dans l'arborescence DOM appelle le nouveau nœud "attach" .

Le traitement des balises HTML et body entraîne la construction de la racine de l'arborescence de rendu. L'objet de rendu racine correspond à ce que la spécification CSS appelle le bloc qui le contient: le bloc situé en haut du bloc qui contient tous les autres blocs. Ses dimensions correspondent à la fenêtre d'affichage, c'est-à-dire aux dimensions de la zone d'affichage de la fenêtre du navigateur. Firefox l'appelle ViewPortFrame et WebKit l'appelle RenderView. Il s'agit de l'objet de rendu vers lequel pointe le document. Le reste de l'arborescence correspond à une insertion de nœuds DOM.

Consultez la spécification CSS2 sur le modèle de traitement.

Calcul de style

Pour créer l'arborescence de rendu, vous devez calculer les propriétés visuelles de chaque objet de rendu. Pour ce faire, nous calculons les propriétés de style de chaque élément.

Le style inclut des feuilles de style d'origines diverses, des éléments de style intégrés et des propriétés visuelles dans le code HTML (comme la propriété "bgcolor").Les versions ultérieures sont converties en propriétés de style CSS correspondantes.

Les feuilles de style proviennent des feuilles de style par défaut du navigateur, c'est-à-dire des feuilles de style fournies par l'auteur de la page et par l'utilisateur. Il s'agit de feuilles de style fournies par l'utilisateur du navigateur (les navigateurs vous permettent de définir vos styles préférés). Dans Firefox, par exemple, il suffit de placer une feuille de style dans le "Profil Firefox" dossier).

Le calcul de style pose quelques difficultés:

  1. Les données de style sont un élément très volumineux contenant de nombreuses propriétés de style, ce qui peut entraîner des problèmes de mémoire.
  2. Identifier les règles de correspondance pour chaque élément peut entraîner des problèmes de performances s'il n'est pas optimisé. Parcourir la liste complète des règles de chaque élément pour trouver des correspondances est une tâche fastidieuse. Les sélecteurs peuvent avoir une structure complexe qui peut entraîner le démarrage du processus de mise en correspondance sur une voie en apparence prometteuse qui s'est avérée inutile, et une autre voie doit être essayée.

    Par exemple, ce sélecteur composé:

    div div div div{
    ...
    }
    

    Cela signifie que les règles s'appliquent à un <div> qui est le descendant de trois div. Supposons que vous souhaitiez vérifier si la règle s'applique à un élément <div> donné. Vous choisissez un chemin spécifique en haut de l'arborescence pour effectuer la vérification. Vous devrez peut-être balayer l'arborescence des nœuds vers le haut pour constater qu'il n'y a que deux div et que la règle ne s'applique pas. Vous devez ensuite essayer d'autres chemins dans l'arborescence.

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

Voyons comment les navigateurs sont confrontés à ces problèmes:

Partager des données de style

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

  1. Les éléments doivent se trouver dans le même état de souris (par exemple, l'un ne peut pas passer la souris sur l'autre, mais pas l'autre).
  2. Aucun des deux éléments ne doit avoir d'identifiant
  3. Les noms des balises doivent correspondre
  4. Les attributs de classe doivent correspondre
  5. L'ensemble d'attributs mappés doit être identique
  6. Les états de l'association doivent correspondre
  7. Les états de sélection doivent correspondre
  8. Aucun de ces éléments ne doit être affecté par les sélecteurs d'attributs. Dans ce cas, les sélecteurs sont considérés comme ayant une correspondance de sélecteur utilisant un sélecteur d'attribut à n'importe quelle position du sélecteur.
  9. Les éléments ne doivent pas comporter d'attribut de style intégré
  10. Aucun sélecteur frère ne doit être utilisé. WebCore génère simplement un commutateur global lorsqu'un sélecteur frère est détecté et désactive le partage des styles pour l'ensemble du document lorsqu'il est présent. Cela inclut le sélecteur + et les sélecteurs tels que :premier-enfant et :dernier-enfant.

Arborescence des règles de Firefox

Firefox dispose de deux arborescences supplémentaires pour faciliter le calcul des styles: l'arborescence des règles et l'arborescence de contexte de style. WebKit comporte également des objets de style, mais ceux-ci ne sont pas stockés dans une arborescence, comme l'arborescence de contexte de style. Seul le nœud DOM pointe vers le style concerné.

Arborescence de contexte de style Firefox.
Figure 14: Arborescence de contexte de style Firefox

Les contextes de style contiennent des valeurs de fin. Les valeurs sont calculées en appliquant toutes les règles de correspondance dans le bon ordre et en effectuant des manipulations qui les transforment de valeurs logiques en valeurs concrètes. Par exemple, si la valeur logique est un pourcentage de l'écran, elle sera calculée et transformée en unités absolues. L'arbre des règles est très astucieux. Il permet de partager ces valeurs entre les nœuds pour éviter de les recalculer. Cela permet également d'économiser de l'espace.

Toutes les règles correspondantes sont stockées dans une arborescence. Les nœuds les plus bas d'un chemin ont une priorité plus élevée. L'arborescence contient tous les chemins d'accès aux règles trouvées. Le stockage des règles n'est pas effectué sans délai. L'arborescence n'est pas calculée au début pour chaque nœud, mais chaque fois qu'un style de nœud doit être calculé, les chemins calculés sont ajoutés à l'arborescence.

L'idée est de considérer les chemins d'arborescence comme des mots dans un lexique. Supposons que nous ayons déjà calculé l'arborescence de règles:

Arborescence des règles calculées
Figure 15: Arborescence des règles calculées

Supposons que nous devions établir une correspondance avec les règles d'un autre élément de l'arborescence de contenu et découvrir que les règles correspondantes (dans le bon ordre) sont B-E-I. Ce chemin est déjà présent dans l'arborescence, car nous avons déjà calculé le chemin A-B-E-I-L. Nous avons désormais moins de travail à faire.

Voyons comment l'arbre nous fait gagner du temps.

Division en objets STRUCT

Les contextes de style sont divisés en structs. Ces structs contiennent des informations de style pour une certaine catégorie, comme la bordure ou la couleur. Toutes les propriétés d'une structure sont héritées ou non. Les propriétés héritées sont celles qui, à moins d'être définies par l'élément, sont héritées de leur parent. Les propriétés non héritées (appelées propriétés "réinitialiser") utilisent des valeurs par défaut si elles ne sont pas définies.

L'arborescence nous aide à mettre en cache des structures entières (contenant les valeurs finales calculées) dans l'arborescence. L'idée est que si le nœud inférieur ne fournit pas de définition pour un struct, un struct mis en cache dans un nœud supérieur peut être utilisé.

Calculer les contextes de style à l'aide de l'arborescence des règles

Lors du calcul du contexte de style d'un élément spécifique, nous calculons d'abord un tracé dans l'arborescence des règles ou nous utilisons un tracé existant. Nous commençons ensuite à appliquer les règles au chemin d'accès pour remplir les structures dans le nouveau contexte de style. Nous commençons par le nœud inférieur du chemin, celui qui a la priorité la plus élevée (généralement le sélecteur le plus spécifique) et balayons l'arborescence jusqu'à ce que l'objet struct soit plein. S'il n'existe aucune spécification pour le struct dans ce nœud de règle, nous pouvons procéder à une optimisation considérable : nous remontons l'arborescence jusqu'à trouver un nœud qui la spécifie entièrement et pointe vers lui. C'est la meilleure optimisation : l'intégralité du struct est partagé. Cela permet d'économiser les calculs des valeurs finales et de la mémoire.

Si nous trouvons des définitions partielles, nous montons dans l'arborescence jusqu'à ce que l'objet STRUCT soit rempli.

Si nous n'avons pas trouvé de définition pour notre struct, au cas où le struct serait "hérité" nous pointons vers le struct du parent dans l'arborescence de contexte. Dans le cas présent, nous avons également réussi à partager des structs. S'il s'agit d'un struct de réinitialisation, les valeurs par défaut seront utilisées.

Si le nœud le plus spécifique ajoute des valeurs, nous devons effectuer des calculs supplémentaires pour le transformer en valeurs réelles. Nous mettons ensuite en cache le résultat dans le nœud de l'arborescence afin qu'il puisse être utilisé par les enfants.

Si un élément a un frère ou une sœur qui pointe vers le même nœud d'arbre, l'intégralité du contexte du style peut être partagé entre eux.

Prenons un exemple: Supposons que nous avons ce code HTML

<html>
  <body>
    <div class="err" id="div1">
      <p>
        this is a <span class="big"> big error </span>
        this is also a
        <span class="big"> very  big  error</span> error
      </p>
    </div>
    <div class="err" id="div2">another error</div>
  </body>
</html>

Et les règles suivantes:

div {margin: 5px; color:black}
.err {color:red}
.big {margin-top:3px}
div span {margin-bottom:4px}
#div1 {color:blue}
#div2 {color:green}

Pour simplifier, supposons que nous ne devions remplir que deux structs: le struct color et le struct margin. La structure de couleur ne contient qu'un seul membre: la couleur Le struct margin contient les quatre côtés.

L'arborescence de règles obtenue se présente comme suit (les nœuds sont signalés par un nom de nœud, c'est-à-dire le numéro de la règle vers laquelle ils pointent) :

Arborescence des règles
Figure 16: Arborescence des règles

L'arborescence de contexte se présentera comme suit (nom du nœud: nœud de règle vers lequel il pointe):

Arborescence de contexte.
Figure 17: Arborescence de contexte

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

Nous mettrons en correspondance les règles et découvrirons que les règles de correspondance pour <div> sont 1, 2 et 6. Cela signifie qu'il existe déjà dans l'arborescence un chemin d'accès que notre élément peut utiliser et qu'il ne nous reste plus qu'à y ajouter un autre nœud pour la règle 6 (le nœud F dans l'arborescence des règles).

Nous allons créer un contexte de style et le placer dans l'arborescence de contexte. Le nouveau contexte de style pointera vers le nœud F de l'arborescence des règles.

Nous devons maintenant remplir les structs de style. Nous allons commencer par remplir le struct margin. Étant donné que le dernier nœud de règle (F) n'ajoute pas de structure à la marge, nous pouvons remonter dans l'arborescence jusqu'à ce que nous trouvions une structure mise en cache calculée lors d'une insertion de nœud précédente et que nous l'utilisions. Nous la trouverons sur le nœud B, qui est le nœud le plus élevé qui a spécifié des règles de marge.

Nous avons une définition pour le struct couleur, nous ne pouvons donc pas utiliser un struct mis en cache. La couleur ayant un attribut, nous n'avons pas besoin de remonter dans l'arborescence pour remplir d'autres attributs. Nous calculerons la valeur finale (convertir la chaîne en RVB, etc.) et mettrons en cache la structure calculée sur ce nœud.

Le travail sur le deuxième élément <span> est encore plus facile. Nous appliquerons les règles et en arriverons à la règle G, comme le segment précédent. Étant donné que nos frères et sœurs pointent vers le même nœud, nous pouvons partager l'intégralité du contexte du style et pointer simplement vers le contexte du segment précédent.

Pour les structures qui contiennent des règles héritées du parent, la mise en cache est effectuée dans l'arborescence de contexte (la propriété de couleur est en fait héritée, mais Firefox la traite comme une réinitialisation et la met en cache dans l'arborescence des règles).

Par exemple, si nous ajoutions des règles pour les polices dans un paragraphe:

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

Ensuite, l'élément de paragraphe, qui est un enfant de l'élément div dans l'arborescence de contexte, peut avoir partagé la même structure de police que son parent. C'est le cas si aucune règle de police n'a été spécifiée pour le paragraphe.

Dans WebKit, qui ne possède pas d'arborescence de règles, les déclarations correspondantes sont balayées quatre fois. Nous appliquons d'abord les propriétés non importantes à priorité élevée (les propriétés qui doivent l'être en premier car d'autres en dépendent, comme les propriétés display), puis les règles de priorité élevée à priorité élevée, puis les règles de priorité normale non importantes, puis les règles de priorité normale. Cela signifie que les propriétés qui apparaissent plusieurs fois seront résolues dans le bon ordre en cascade. Le dernier l'emporte.

En résumé, le partage des objets de style (en totalité ou en partie des structs qu'ils contiennent) permet de résoudre les problèmes 1 et 3. L'arborescence des règles de Firefox permet également d'appliquer les propriétés dans le bon ordre.

Manipuler les règles pour faciliter la mise en correspondance

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

  1. Les règles CSS, que ce soit dans des feuilles de style externes ou dans des éléments de style. css p {color: blue}
  2. Les attributs de style intégrés tels que html <p style="color: blue" />
  3. Attributs visuels HTML (associés aux règles de style pertinentes) html <p bgcolor="blue" /> Les deux derniers peuvent être facilement mis en correspondance avec l'élément, car il est propriétaire des attributs de style et les attributs HTML peuvent être mappés en utilisant l'élément comme clé.

Comme indiqué précédemment dans le problème 2, la mise en correspondance des règles CSS peut s'avérer plus délicate. Pour résoudre ce problème, les règles sont manipulées pour en faciliter l'accès.

Une fois la feuille de style analysée, les règles sont ajoutées à l'une des différentes cartes de hachage, en fonction du sélecteur. Il existe des mappages par ID, par nom de classe et par nom de tag, ainsi qu'un mappage général pour tout ce qui n'entre pas dans ces catégories. Si le sélecteur est un ID, la règle sera ajoutée au mappage d'ID. S'il s'agit d'une classe, elle sera ajoutée au mappage de classes, etc.

Cette manipulation facilite grandement la mise en correspondance des règles. Il n'est pas nécessaire d'examiner chaque déclaration: nous pouvons extraire les règles pertinentes d'un élément à partir des cartes. Cette optimisation élimine plus de 95% des règles, de sorte qu'elles n'ont même pas besoin d'être prises en compte au cours du processus de mise en correspondance(4.1).

Voyons par exemple les règles de style suivantes:

p.error {color: red}
#messageDiv {height: 50px}
div {margin: 5px}

La première règle sera insérée dans le mappage de classes. Le deuxième dans le mappage d'ID et le troisième dans celui des tags.

Pour le fragment HTML suivant :

<p class="error">an error occurred</p>
<div id=" messageDiv">this is a message</div>

Nous allons d'abord essayer de trouver des règles pour l'élément "p". La carte des classes contiendra une "erreur" clé sous laquelle la règle pour "p.error" est trouvé. L'élément div aura des règles pertinentes dans le mappage d'ID (la clé correspond à l'ID) et le mappage de tags. Il ne vous reste donc plus qu'à déterminer quelles règles extraites par les clés correspondent vraiment.

Par exemple, si la règle pour l'élément div était:

table div {margin: 5px}

Elle sera tout de même extraite du mappage de tags, car la clé est le sélecteur le plus à droite, mais elle ne correspond pas à notre élément div, qui n'a pas d'ancêtre de table.

WebKit et Firefox effectuent tous deux cette manipulation.

Ordre en cascade de la feuille de style

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

Le problème commence quand il y a plusieurs définitions. Voici l'ordre en cascade pour résoudre le problème.

Une déclaration pour une propriété de style peut apparaître dans plusieurs feuilles de style et plusieurs fois dans une feuille de style. L'ordre d'application des règles est donc très important. C'est ce qu'on appelle la « en cascade » commande. Selon la spécification CSS2, l'ordre en cascade est le suivant (du plus bas au plus élevé):

  1. Déclarations du navigateur
  2. Déclarations normales de l'utilisateur
  3. Déclarations normales de l'auteur
  4. Déclarations importantes concernant l'auteur
  5. Déclarations importantes de l'utilisateur

Les déclarations du navigateur sont les moins importantes et l'utilisateur ne remplace l'auteur que si la déclaration a été marquée comme importante. Les déclarations ayant le même ordre sont triées par spécification, puis par ordre de spécification. Les attributs visuels HTML sont traduits en déclarations CSS correspondantes . Elles sont considérées comme des règles d'auteur de faible priorité.

Spécificité

La spécificité du sélecteur est définie par la spécification CSS2, comme suit:

  1. compte 1 si la déclaration dont il provient est un "style" plutôt qu'une règle avec un sélecteur, ou la valeur 0 dans le cas contraire (= a).
  2. Compter le nombre d'attributs d'identifiant dans le sélecteur (= b)
  3. Compter le nombre d'autres attributs et pseudo-classes dans le sélecteur (= c)
  4. 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 avec une base large) permet d'obtenir la spécificité.

La base numérique 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 une base hexadécimale. Dans le cas peu probable où a=17 vous aurez besoin d'une base numérique à 17 chiffres. La situation la plus tardive peut se produire avec un sélecteur comme celui-ci: html body div div p... (17 tags dans votre sélecteur... peu probable).

Voici quelques exemples :

 *             {}  /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
 li            {}  /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
 li:first-line {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
 ul li         {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
 ul ol+li      {}  /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
 h1 + *[rel=up]{}  /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
 ul ol li.red  {}  /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
 li.red.level  {}  /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
 #x34y         {}  /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
 style=""          /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */

Trier les règles

Une fois les règles mises en correspondance, elles sont triées en fonction des règles de cascade d'annonces. WebKit utilise le tri à bulles pour les petites listes et le tri par fusion pour les grandes listes. WebKit implémente le tri en remplaçant l'opérateur > pour les règles:

static bool operator >(CSSRuleData& r1, CSSRuleData& r2)
{
    int spec1 = r1.selector()->specificity();
    int spec2 = r2.selector()->specificity();
    return (spec1 == spec2) : r1.position() > r2.position() : spec1 > spec2;
}

Processus progressif

WebKit utilise un indicateur qui indique si toutes les feuilles de style de premier niveau (y compris @imports) ont été chargées. Si le style n'est pas entièrement chargé lors de l'association, des espaces réservés sont utilisés et sont indiqués dans le document. Ils seront recalculés une fois les feuilles de style chargées.

Mise en page

Lorsque le moteur de rendu est créé et ajouté à l'arborescence, il n'a pas de position ni de taille. Le calcul de ces valeurs est appelé mise en page ou reflow.

HTML utilise un modèle de mise en page basé sur le flux, ce qui signifie que la plupart du temps, il est possible de calculer la géométrie en un seul passage. Éléments plus tard "dans le flux" n'ont généralement pas d'incidence sur la géométrie des éléments situés plus tôt dans le flux. La mise en page peut donc se poursuivre de gauche à droite, de haut en bas dans le document. Il existe des exceptions: par exemple, les tableaux HTML peuvent nécessiter plusieurs passes.

Le système de coordonnées est relatif à l'image racine. Les coordonnées du haut et de la gauche sont utilisées.

La mise en page est un processus récursif. Elle commence au niveau du moteur de rendu racine, qui correspond à l'élément <html> du document HTML. La mise en page se poursuit de manière récursive dans une partie ou la totalité de la hiérarchie des frames, en calculant des informations géométriques pour chaque moteur de rendu qui en a besoin.

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

Tous les moteurs de rendu ont une "mise en page" ou "reflow" , chaque moteur de rendu appelle la méthode de mise en page de ses enfants qui ont besoin d'une mise en page.

Système de bits sales

Pour ne pas avoir une mise en page complète à chaque petite modification, les navigateurs utilisent un "dirty bit" du système d'exploitation. Un moteur de rendu modifié ou ajouté se marque, ainsi que ses enfants, comme "dirty" : mise en page nécessaire.

Deux indicateurs sont affichés : "sale" et "les enfants sont sales". ce qui signifie que même si le moteur de rendu lui-même fonctionne correctement, il comporte au moins un enfant qui nécessite une mise en page.

Mise en page globale et incrémentielle

La mise en page peut être déclenchée sur l'intégralité de l'arborescence de rendu, qui est "globale". mise en page. Cela peut se produire pour les raisons suivantes:

  1. Il s'agit d'un changement de style global qui affecte tous les moteurs de rendu (par exemple, un changement de taille de police).
  2. Suite au redimensionnement d'un écran

La mise en page peut être incrémentielle. Seuls les moteurs de rendu sales seront disposés (cela peut entraîner des dommages qui nécessiteront des mises en page supplémentaires).

La mise en page incrémentielle est déclenchée (de manière asynchrone) lorsque les moteurs de rendu sont incorrects. Par exemple, lorsque de nouveaux moteurs de rendu sont ajoutés à l'arborescence de rendu après que du contenu supplémentaire provient du réseau et a été ajouté à l'arborescence DOM.

Mise en page incrémentielle.
Figure 18: Mise en page incrémentielle (seuls les moteurs de rendu incorrects et leurs enfants sont disposés)

Mise en page asynchrone et synchrone

La mise en page incrémentielle s'effectue de manière asynchrone. "Commandes de reflow" dans les files d'attente Firefox pour les mises en page incrémentielles et un planificateur déclenche l'exécution par lots de ces commandes. WebKit dispose également d'un minuteur qui exécute une mise en page incrémentielle, l'arborescence étant balayée et "sale" sont mis en page.

Scripts demandant des informations de style, comme "offsetHeight" peut déclencher une mise en page incrémentielle de manière synchrone.

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

Parfois, la mise en page est déclenchée en tant que rappel après une mise en page initiale, car certains attributs, comme la position de défilement, ont changé.

Optimisations

Lorsqu'une mise en page est déclenchée par un "redimensionnement" ou un changement de position(et non de taille), les tailles de rendu sont extraites d'un cache et ne sont pas recalculées...

Dans certains cas, seule une sous-arborescence est modifiée et la mise en page ne commence pas à la racine. Cela peut se produire dans les cas où la modification est locale et n'affecte pas son environnement, par exemple du texte inséré dans des champs de texte (sinon, chaque frappe déclencherait une mise en page à partir de la racine).

Processus de mise en page

La mise en page présente généralement le format suivant:

  1. Le moteur de rendu parent détermine sa propre largeur.
  2. Le parent s'occupe de ses enfants et:
    1. Placez le moteur de rendu enfant (définit son x et son y).
    2. Appelle la mise en page enfant si nécessaire (elle est sale, dans une mise en page globale ou pour une autre raison), ce qui calcule la hauteur de l'enfant.
  3. Le parent utilise les hauteurs cumulatives des enfants ainsi que les hauteurs des marges et des marges intérieures pour définir sa propre hauteur. Le parent du moteur de rendu parent utilise cette valeur.
  4. Définit son élément sale sur "false".

Firefox utilise un "état" objet(nsHTMLReflowState) en tant que paramètre de la mise en page (appelé "reflow"). L'état inclut, entre autres, la largeur des parents.

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

Calcul de la largeur

La largeur du moteur de rendu est calculée à l'aide de la largeur du bloc de conteneur et du style "width" du moteur de rendu. , les marges et les bordures.

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

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

Serait calculé par WebKit comme suit(méthode de classe RenderBox calcWidth):

  • La largeur du conteneur correspond à la valeur maximale des conteneurs availableWidth et 0. Dans ce cas, la valeur availableWidth correspond à la valeur contentWidth. Elle est calculée comme suit:
clientWidth() - paddingLeft() - paddingRight()

clientWidth et clientHeight représentent l'intérieur d'un objet. sans la bordure ni la barre de défilement.

  • La largeur des éléments est la « largeur » attribut de style. Elle sera calculée sous la forme d'une valeur absolue en calculant le pourcentage de la largeur du conteneur.

  • Les bordures horizontales et les marges intérieures sont maintenant ajoutées.

Jusqu'à présent, il s'agissait du calcul de la "largeur préférée". Les largeurs minimale et maximale seront maintenant calculées.

Si la largeur souhaitée est supérieure à la largeur maximale, la largeur maximale est utilisée. Si elle est inférieure à la largeur minimale (la plus petite unité inviolable), la largeur minimale est utilisée.

Les valeurs sont mises en cache si une mise en page est nécessaire, mais la largeur ne change pas.

Saut de ligne

Lorsqu'un moteur de rendu au milieu d'une mise en page décide qu'il doit être endommagé, il s'arrête et se propage au parent de la mise en page qu'il doit être endommagé. Le parent crée les moteurs de rendu supplémentaires et appelle la mise en page sur ceux-ci.

Peinture

À l'étape de peinture, l'arborescence de rendu est balayée et la fonction "paint()" du moteur de rendu est appelée pour afficher du contenu à l'écran. Painting utilise le composant d'infrastructure de l'UI.

Mondial et incrémentiel

Tout comme la mise en page, la peinture peut être globale (l'arbre entier est peint) ou incrémentielle. Lors de la mise en peinture incrémentielle, certains moteurs de rendu sont modifiés sans que cela n'affecte l'ensemble de l'arborescence. Le moteur de rendu modifié invalide son rectangle à l'écran. Le système d'exploitation la considère alors comme une "région sale" et générer une "peinture" . Le système d'exploitation le fait de manière intelligente et fusionne plusieurs régions en une seule. Dans Chrome, c'est plus compliqué, car le processus du moteur de rendu est différent de celui du processus principal. Chrome simule le comportement du système d'exploitation dans une certaine mesure. La présentation écoute ces événements et délègue le message à la racine de rendu. L'arborescence est balayée jusqu'à ce que le moteur de rendu approprié soit atteint. Il se repeindra tout seul (et généralement ses enfants).

L'ordre de peinture

CSS2 définit l'ordre du processus de peinture. Il s'agit en réalité de l'ordre dans lequel les éléments sont empilés dans les contextes d'empilement. Cet ordre affecte la peinture, car les piles sont peintes de l'arrière vers l'avant. L'ordre d'empilement d'un moteur de rendu de blocs est le suivant:

  1. couleur de l'arrière-plan
  2. Image de fond
  3. border
  4. enfants
  5. vue générale

Liste d'affichage Firefox

Firefox passe en revue l'arborescence de rendu et crée une liste d'affichage pour le rectangle peint. Il contient les moteurs de rendu correspondant au rectangle, dans l'ordre de peinture approprié (arrière-plans des moteurs de rendu, bordures, etc.).

Ainsi, l'arborescence doit être balayée une seule fois pour être repeinte, et non plusieurs fois : tous les arrière-plans, toutes les images, puis toutes les bordures, etc.

Firefox optimise le processus en n'ajoutant pas d'éléments qui seront cachés, comme les éléments complètement sous d'autres éléments opaques.

Stockage rectangulaire WebKit

Avant de repeindre, WebKit enregistre l'ancien rectangle en tant que bitmap. Il ne peint ensuite que le delta entre les nouveaux rectangles et les anciens.

Modifications dynamiques

Les navigateurs essaient d'effectuer les actions les plus minimes possibles en réponse à un changement. Ainsi, les modifications apportées à la couleur d'un élément n'entraînent qu'une nouvelle peinture de l'élément. Les modifications apportées à la position de l'élément entraînent la mise en page et l'application d'une nouvelle peinture de l'élément, de ses enfants et éventuellement de ses frères et sœurs. L'ajout d'un nœud DOM entraîne la mise en page et l'application d'une nouvelle peinture du nœud. Modifications importantes, comme l'augmentation de la taille de police du code "html" entraîne l'invalidation des caches ainsi que la remise en page et l'application d'une nouvelle peinture sur l'ensemble de l'arborescence.

Threads du moteur de rendu

Le moteur de rendu est monothread. Presque tout, à l'exception des opérations réseau, se produit dans un seul thread. Dans Firefox et Safari, il s'agit du thread principal du navigateur. Dans Chrome, il s'agit du thread principal du traitement des onglets.

Les opérations réseau peuvent être effectuées par plusieurs threads parallèles. Le nombre de connexions parallèles est limité (généralement 2 à 6 connexions).

Boucle d'événements

Le thread principal du navigateur est une boucle d'événements. Il s'agit d'une boucle infinie qui maintient le processus actif. Il attend les événements (tels que les événements de mise en page et de peinture) et les traite. Voici le code Firefox pour la boucle d'événements principale:

while (!mExiting)
    NS_ProcessNextEvent(thread);

Modèle visuel CSS2

La toile

Conformément à la spécification CSS2, Le terme canevas décrit "l'espace où la structure de mise en forme est affichée", c'est-à-dire l'endroit où le navigateur peint le contenu.

Le canevas est infini pour chaque dimension de l'espace, mais les navigateurs choisissent une largeur initiale en fonction des dimensions de la fenêtre d'affichage.

Selon le site www.w3.org/TR/CSS2/zindex.html, le canevas est transparent s'il est contenu dans un autre. Si ce n'est pas le cas, une couleur définie par le navigateur lui est attribuée.

Modèle de zone de CSS

Le modèle de zone CSS décrit les zones rectangulaires générées pour les éléments de l'arborescence de documents et disposées en fonction du modèle de mise en forme visuelle.

Chaque zone comporte une zone de contenu (par exemple, du texte, une image, etc.) et des marges intérieures, des bordures et des marges facultatives.

Modèle de zone CSS2
Figure 19: Modèle de zone CSS2

Chaque nœud génère 0...n cadres de ce type.

Tous les éléments ont une fonction "display" qui détermine le type de zone qui sera générée.

Exemples :

block: generates a block box.
inline: generates one or more inline boxes.
none: no box is generated.

Par défaut, il est intégré, mais la feuille de style du navigateur peut définir d'autres valeurs par défaut. Par exemple, l'affichage par défaut du tag "div" est "block".

Vous trouverez un exemple de feuille de style par défaut à l'adresse suivante: www.w3.org/TR/CSS2/sample.html.

Schéma de positionnement

Il existe trois schémas:

  1. Normale: l'objet est positionné en fonction de sa position dans le document. Cela signifie que sa position dans l'arborescence d'affichage est identique à sa position dans l'arborescence DOM, et qu'elle est disposée en fonction de son type de zone et de ses dimensions.
  2. Flottant: l'objet est d'abord disposé comme un flux normal, puis déplacé aussi loin vers la gauche ou vers la droite que possible
  3. Absolue: l'objet est placé dans l'arborescence de rendu à un autre endroit que dans l'arborescence DOM.

Le schéma de positionnement est défini par la valeur "position" et la valeur "float" .

  • les valeurs statiques et relatives provoquent un flux normal
  • les valeurs absolues et fixes provoquent un positionnement absolu

En cas de positionnement statique, aucune position n'est définie et le positionnement par défaut est utilisé. Dans les autres schémas, l'auteur spécifie la position: haut, bas, gauche, droite.

La disposition de la boîte est déterminée par:

  • Type de zone
  • Dimensions de la boîte
  • Schéma de positionnement
  • Informations externes telles que la taille de l'image et de l'écran

Types de boîtes

Bloc: forme d'un volume et possédant son propre rectangle dans la fenêtre du navigateur.

Zone de blocage.
Figure 20: Blocs

Boîtier intégré: ne possède pas son propre bloc, mais se trouve dans un bloc conteneur.

Boîtes intégrées.
Figure 21: Boîtiers intégrés

Les blocs sont formatés verticalement l'un après l'autre. Les lignes intégrées sont mises en forme horizontalement.

Mise en forme bloquée et mise en forme intégrée
Figure 22: Mise en forme bloquée et intégrée

Les zones intégrées sont placées à l'intérieur de lignes ou "cadres de ligne". Les lignes sont au moins aussi hautes que le cadre le plus haut, mais elles peuvent l'être plus s'ils sont alignés sur la ligne de référence. c'est-à-dire que la partie inférieure d'un élément est alignée en un point d'une autre boîte, autre que le bas. Si la largeur du conteneur n'est pas suffisante, les lignes sont placées sur plusieurs lignes. C'est généralement ce qui se produit dans un paragraphe.

Lignes.
Figure 23: Lignes

La

Relative

Positionnement relatif : positionné comme d'habitude, puis déplacé selon le delta requis.

Positionnement relatif.
Figure 24: Positionnement relatif

Nombres décimaux

Une zone flottante est décalée vers la gauche ou la droite d'une ligne. Ce qui est intéressant, c'est que les autres cadres circulent autour de celui-ci. Le code HTML:

<p>
  <img style="float: right" src="images/image.gif" width="100" height="100">
  Lorem ipsum dolor sit amet, consectetuer...
</p>

Cet élément se présentera comme suit:

Propriété « float ».
Figure 25: Flottant

Absolue 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 relatives au conteneur. Dans "Fixe", le conteneur est la fenêtre d'affichage.

Positionnement fixe.
Figure 26: Positionnement fixe

Représentation en couches

Ceci est spécifié par la propriété CSS "z-index". Elle représente la troisième dimension de la boîte: sa position le long de l'axe z.

Les boîtes sont divisées en piles (appelées contextes d'empilement). Dans chaque pile, les éléments arrière seront peints en premier et les éléments suivants en haut, plus près de l'utilisateur. En cas de chevauchement, l'élément de premier plan masque l'élément précédent.

L'ordre des piles dépend de la propriété z-index. Blocs avec "z-index" pour former une pile locale. La fenêtre d'affichage contient la pile externe.

Exemple :

<style type="text/css">
  div {
    position: absolute;
    left: 2in;
    top: 2in;
  }
</style>

<p>
  <div
    style="z-index: 3;background-color:red; width: 1in; height: 1in; ">
  </div>
  <div
    style="z-index: 1;background-color:green;width: 2in; height: 2in;">
  </div>
</p>

Le résultat sera le suivant:

Positionnement fixe.
Figure 27: Positionnement fixe

Bien que le div rouge précède le vert dans le balisage et qu'il ait été peint auparavant dans le flux normal, la propriété z-index est plus haute. Elle apparaît donc plus en avant dans la pile détenue par la zone racine.

Ressources

  1. Architecture du navigateur

    1. Alan Grosskurth. Architecture de référence pour les navigateurs Web (PDF)
    2. Gupta, Vineet. Fonctionnement des navigateurs - Partie 1 - Architecture
  2. Analyse

    1. Aho, Sethi, Ullman, Compilateurs: principes, techniques et outils (aussi appelé "livre du dragon"), Addison-Wesley, 1986
    2. Rick Jelliffe. Ce qu'il y a de plus beau et d'audace: deux nouvelles suggestions pour HTML 5.
  3. Firefox

    1. L. David Baron, Accelerate HTML and CSS: Layout Engine Internals for Web Developers.
    2. L. David Baron, Accelerate HTML and CSS: Layout Engine Internals for Web Developers (Google Tech talk video)
    3. L. David Baron, moteur de mise en page de Mozilla
    4. L. David Baron, documentation Mozilla Style System
    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(partie 1)
    2. David Hyatt, An Overview of WebCore
    3. David Hyatt, WebCore Rendering
    4. David Hyatt, The FOUC Problem
  5. Spécifications W3C

    1. Spécifications HTML 4.01
    2. Spécification HTML5 W3C
    3. Spécification des feuilles de style en cascade de niveau 2, révision 1 (CSS 2.1)
  6. Instructions de création de navigateurs

    1. Firefox : https://developer.mozilla.org/Build_Documentation
    2. WebKit http://webkit.org/building/build.html

Traductions

Cette page a été traduite deux fois en japonais:

Vous pouvez afficher les traductions hébergées en externe Coréen et Turc.

Bravo à tous !