diff --git a/content/tutorials/internals/howbrowserswork/fr/index.html b/content/tutorials/internals/howbrowserswork/fr/index.html new file mode 100644 index 000000000..1a35f9ba5 --- /dev/null +++ b/content/tutorials/internals/howbrowserswork/fr/index.html @@ -0,0 +1,2228 @@ +{% extends "tutorial.html" %} + +{% block iscompatible %} + +{% endblock %} +{% block head %} + + +{% endblock %} + + +{% block content %} +
+ Cette introduction détaillée sur les opérations internes de WebKit et Gecko est le résultat de beaucoup de + recherches menées par la développeuse israélienne Tali Garsiel. Durant quelques années, elle a examiné l'ensemble + des données publiées sur les composants internes des navigateurs + (voir les ressources) + et passé beaucoup de temps à lire le code source de navigateurs. Elle écrit : + +
+ À l'époque de la domination d'IE à 90% il n'y avait rien d'autre à faire que de considérer le navigateur comme une + "boîte noire", mais aujourd'hui, avec les navigateurs open représentant plus de la moitié des utilisateurs du marché, c'est + le bon moment pour jeter un œil sous le capot et regarder ce qui se trouve dans un navigateur. Eh bien, ce qu'on y + trouve sont des millions de lignes de C++... ++ Tali a publié ses recherches sur son site, mais nous savions qu'elles méritaient + une plus large audience, et l'avons donc revu et republié ici. + +
+ En tant que développeur web, apprendre l'interne des opérations d'un navigateur vous aide à prendre de + meilleures décisions et à comprendre les justifications derrière l'apparition de bonnes pratiques. Bien + qu'il s'agisse d'un document plutôt conséquent, nous vous recommandons de passer un peu de temps à le creuser ; + soyez sûr(e) que vous ne le regretterez pas. + + Paul Irish, Relations Développeurs de Chrome +
+ + ++ Cet article a été traduit en différentes langues : HTML5 Rocks héberge les versions en anglais, allemand, espagnol, japonais, portugais, russe et chinois simplifié. Vous pouvez + également lire des traductions stockées ailleurs en coréen et truc . +
+ Vous pouvez aussi regarder Tali Garsiel donnant une conférence sur ce + sujet sur Vimeo: + +
+
+ Les navigateurs web sont les logiciels les plus utilisés. Dans cette introduction, j'expliquerai comment ils
+ fonctionnent en réalité. Nous verrons ce qui se passe lorsque vous tapez google.com
dans la barre
+ d'adresse jusqu'à ce qu'apparaisse la page Google sur l'écran du navigateur.
+
+ Cinq grands navigateurs sont utilisés sur les ordinateurs de bureau aujourd'hui : Chrome, Internet Explorer, + Firefox, Safari et Opera. Sur mobile, les principaux navigateurs sont Android Browser, iPhone, Opera Mini et Opera + Mobile, UC Browser, les navigateurs du Nokia S40/S60 browsers et Chrome–tous, à l'exception des navigateurs + Opéra, étant basés sur WebKit. Je donnerai des exemples des navigateurs open source Firefox et Chrome, ainsi que + Safari (qui est en partie open source). Selon StatCounter statistics (en Juin 2013) Chrome, Firefox et Safari représentent + près de 71% de l'utilisation globale des navigateurs sur ordinateur de bureau. Sur mobile, Android Browser, iPhone + et Chrome constituent près de 54% de cette utilisation. +
+ ++ La fonctionnalité principale d'un navigateur est de vous présenter la ressource web que vous avez choisie, en la + demandant au serveur et en l'affichant dans la fenêtre de navigation. La ressource en question est généralement un + document HTML, mais pourrait aussi être un PDF, une image, ou n'importe quel autre type de contenu. La localisation + de la ressource est spécifiée par l'utilisateur au travers d'un URI (Identificateur Uniforme de Ressource). +
++ La manière dont le navigateur interprète et affiche les fichiers HTML est décrite dans les spécifications HTML et + CSS. Ces spécifications sont maintenues par l'organisation W3C (Consortium World Wide Web), qui est + l'organisation régissant les standards du web. Durant des années les navigateurs ne se sont conformés qu'à une + partie des specifications et ont développé leurs propres extensions. Cela a causé de sérieux problèmes de + compatibilité pour les auteurs de pages web. Aujourd'hui la plupart des navigateurs se conforment plus ou moins à + ces specifications. +
++ Les interfaces utilisateur d'un navigateur se ressemblent beaucoup entre elles. Parmi les éléments les plus courants + d'interface utilisateur, on trouve : +
+ Étonnamment, l'interface utilisateur du navigateur n'est spécifiée dans aucune spécification formelle ; elle émane + just de bonnes pratiques forgées au cours d'années d'expérience et de navigateurs s'imitant les uns-les autres. La + spécification HTML5 ne définit pas les éléments d'IU que doit posséder un navigateur, mais liste certains éléments + courants. Parmi ceux-ci, la barre d'adresse, la barre de statut et la barre d'outils. Il existe, bien sûr, des + fonctionnalités spécifiques à un navigateur donné, comme le gestionnaire de téléchargements de Firefox par exemple. +
++ Les composants principaux d'un navigateur sont (1.1): +
+ ++ Il est important de noter que les navigateurs comme Chrome lancent plusieurs instances du moteur de rendu : une pour + chaque onglet. Chaque onglet s'exécute dans un processus distinct. +
+ ++ La responsabilité du moteur de rendu est, eh bien... de faire le rendu, c'est-à-dire d'afficher le contenu demandé + dans la fenêtre du navigateur. +
++ Par défaut le moteur de rendu peut afficher des documents HTML et XML, ainsi que des images. Il peut afficher + d'autres types de données à l'aide de plug-ins ou d'extensions ; par exemple, afficher des documents PDF en + utilisant un plug-in de visualisation PDF. Cependant, dans ce chapitre nous nous concentrerons sur le cas + d'utilisation principal : afficher du HTML et des images, formatés en utilisant CSS.
+ ++ Différents navigateurs utilisent des moteurs de rendu différents : Internet Explorer utilise Trident, Firefox + utilise Gecko, Safari utilise WebKit. Chrome et Opera (depuis la version 15) utilisent Blink, un dérivé de WebKit. +
++ WebKit est un moteur de rendu open source qui a commencé comme moteur pour la plate-forme Linux puis a été modifié + par Apple pour supporter Mac et Windows. Voir webkit.org pour plus de détails. +
+ ++ Le moteur de rendu commencera à récupérer le contenu du document demandé en le demandant à la couche réseau. Cela + sera généralement fait par portions de 8 Ko. +
++ Après cela, il s'agit du flux de base du moteur de rendu : +
+ ++ Le moteur de rendu commencera à analyser syntaxiquement le document HTML et à convertir les éléments en nœuds DOM dans un arbre appelé "arbre de contenu". Le moteur analysera syntaxiquement les données de + style, qu'elles viennent de fichiers CSS externes ou d'éléments de style dans le contenu. Les informations de style, + couplées aux instructions visuelles du HTML, seront utilisés pour créer un autre arbre : l'arbre de rendu. +
++ L'arbre de rendu contient des rectangles avec des attributs visuels comme la couleur et les dimensions. Les + rectangles sont dans le bon ordre pour être affichés à l'écran. +
++ Après sa construction, l'arbre de rendu passe par un processus de "layout". Cela veut dire + donner à chaque nœud les coordonnées exactes de là où il devrait apparaître à l'écran. L'étape suivante est de peindre–l'arbre de rendu va être traversé et chaque nœud sera dessiné en utilisant la + couche de backend IU. +
++ Il est important de comprendre qu'il s'agit d'un processus progressif. Pour une meilleure expérience utilisateur, le + moteur de rendu essaiera d'afficher du contenu à l'écran aussi tôt que possible. Il n'attendra pas que tout le HTML + soit analysé avant de construire les arbres de layout et de rendu. Des parties du contenu seront analysées et + affichées, tandis que le processus continuera avec le reste du contenu qui continue à arriver du réseau. +
+ ++ Les figures 3 et 4 vous montrent que bien que WebKit et Gecko utilisent une terminologie légèrement différente, le + flux reste globalement le même. +
+ Gecko appelle "arbre d'image" l'arbre des éléments visuellement formaté. Chaque élément est une image à afficher. + WebKit utilise le terme de "arbre de rendu", constitué d'"objets de rendu". WebKit utilise le terme de "layout" pour + placer les éléments, tandis que Gecko parle de "Reflux". "Attachement" est le terme utilisé de WebKit pour faire le + lien entre nœuds DOM et information visuelle afin de créer l'arbre de rendu. Une différence non sémantique mineure + est que Gecko dispose d'une couche supplémentaire entre le HTML et l'arbre DOM tree. Il s'agit de "l'entonnoir à + contenu" qui est une fabrique d'éléments DOM. Nous allons parler de chacune des parties du flux : +
+
+ L'analyse syntaxique étant un processus particulièrement significatif au sein du moteur de rendu, nous allons + l'examiner un peu plus en détails. Commençons par une petite introduction sur ce type d'analyse. +
++ L'analyse syntaxique d'un document implique de le traduire en une structure que le code peut utiliser. Le résultat + d'une analyse est généralement un arbre de nœuds représentant la structure du document. On l'appelle arbre d'analyse + ou arbre syntaxique. +
+ ++ Par exemple, analyser l'expression 2 + 3 - 1 pourrait retourner cet arbre : + +
+ + ++ L'analyse syntaxique est basée sur les règles syntaxiques auxquelles obéit le document : le langage ou le format + dans lequel il a été rédigé. Chaque format que vous analysez doit avoir une grammaire déterministe constituée d'un + vocabulaire et de règles de syntaxe. On parle de grammaire non contextuelle. Les langues humaines ne sont pas de tels langages et + ne peuvent donc pas être analysées avec des techniques d'analyse syntaxique conventionnelles. +
++ L'analyse peut être séparée en deux sous-processus : l'analyse lexicale et l'analyse syntaxique. +
++ L'analyse lexicale est le processus de décomposition de l'entrée en tokens. Les tokens représentent le vocabulaire + du langage : la collection des blocs de construction valides. Pour une langue humaine il s'agira de tous les mots + que l'on peut trouver dans un dictionnaire de cette langue. +
++ L'analyse syntaxique est le fait d'appliquer les règles de syntaxe du langage. +
++ Les parsers divisent généralement le travail en deux composants : le lexer (parfois appelé tokenizer) qui est + responsable de décomposer l'entrée en tokens valides, et le parseur qui est responsable de la construction de + l'arbre d'analyse en analysant la structure du document en fonction des règles de syntaxe du langage. + + Le lexeur sait comment éliminer les caractères à ne pas prendre en compte comme les espaces et les sauts de ligne. +
+ ++ Le processus d'analyse est itératif. Le parseur demandera généralement au lexeur un nouveau token et essaiera de + faire correspondre le token à une des règles syntaxiques. Si une règle correspond, un nœud correspondant au token + sera ajouté à l'arbre d'analyse et le parseur demandera un autre token. +
+ Si aucune règle ne correspond, le parseur stockera le token en interne, et continuera à demander d'autres tokens + jusqu'à ce qu'une règle correspondant à l'ensemble des tokens stockés en interne soit trouvée. Si aucune règle n'est + trouvée alors le parseur lèvera une exception. Cela signifie que le document n'était pas valide et contenait des + erreurs de syntaxe. +
++ Dans de nombreux cas l'arbre d'analyse n'est pas le produit final. L'analyse est souvent utilisée pour une + traduction : transformer le document d'entrée en un autre format. Un exemple est la compilation. La compilateur qui + compile le code source en code machine commence par l'analyser sous forme d'un arbre d'analyse, puis traduit l'arbre + en un document de code machine. +
+ + ++ 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 voyons le processus d'analyse. +
+
+ Vocabulaire : Notre langage peut inclure des entiers, des signes plus et des signes moins. +
++ Syntaxe : +
+ Analysons l'entrée 2 + 3 - 1.
La première sous-chaîne qui correspond à une règle est 2
+ : selon la règle #5 il s'agit d'un terme. La second correspondance est 2 + 3 : cela correspond à la
+ troisième règle : un terme suivi d'une opération, suivie d'un autre terme. La correspondance suivante n'interviendra
+ que lorsque la fin de l'entrée sera atteinte. 2 + 3 - 1 est une expression parce que nous savons déjà que
+ 2 + 3 est un terme, et nous avons donc un terme suivi d'une opération, suivie d'un autre terme. 2 +
+ + ne correspondra à aucune règle et est donc une entrée invalide.
+
+ Le vocabulaire est généralement exprimée au travers expressions + régulières. +
++ Par exemple notre langage sera défini comme : +
+ENTIER: 0|[1-9][0-9]* +PLUS: + +MOINS: - ++ 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 . Our language will be defined as: +
+expression := terme opération terme +opération := PLUS | MOINS +terme := ENTIER | expression ++
+ Nous avons dit qu'un langage peut être analysé par des analyseurs syntaxiques réguliers si sa grammaire est une grammaire non contextuelle. Une définition intuitive d'une grammaire non contextuelle + est une grammaire qui peut être entièrement exprimée en BNF. Pour une définition formelle, voir l'article de Wikipedia sur les grammaires non + contextuelles +
+ Il existe deux types d'analyseurs : les analyseurs descendants et les analyseurs ascendants. Une explication + intuitive est que les analyseurs descendants examinent la structure de haut niveau de la syntaxe et tentent de + trouver une règle qui corresponde. Les analyseurs ascendants commençent par l'entrée et la transforment + progressivement dans les règles de syntaxe, en commeçant par les règles de bas niveau, jusqu'à trouver des + correspondance avec les règles de haut niveau. +
++ Voyons comme les deux types d'analyseurs syntaxiques vont traiter notre exemple. +
++ L'analyseur descendant va commencer par la règle de plus haut niveau : il va identifier 2 + 3 comme une + expression. Il va alors identifier 2 + 3 - 1 comme une expression (le processus d'identifier + l'expression évolue, en faisant correspondre les autres règles, mais le point de départ est la règle de plus haut + niveau). +
++ L'analyseur ascendant va scruter l'entrée jusqu'à ce qu'une règle corresponde. Il va alors remplacer l'entrée + correspondante par la règle. Cela va continuer ainsi jusqu'à la fin de l'entrée. L'expression correspondant + partiellement est placée sur la pile de l'analyseur. +
Pile | +Entrée | +
---|---|
+ | 2 + 3 - 1 | +
terme | ++ 3 - 1 | +
terme opération | +3 - 1 | +
expression | +- 1 | +
expression opération | +1 | +
expression | +- | +
+ Des outils existent pour générer un analyseur syntaxiques. Vous leur donner la grammaire de votre langage–son + vocabulaire et ses règles de syntaxe–et ils génèrent un analyseur opérationnel. Créer un parseur demande une + compréhension approfondie de l'analyse syntaxique et il n'est pas aisé de créer un analyseur syntaxique soi-même ; + les générateurs de tels analyseurs sont donc très utiles. +
++ WebKit utilise deux générateurs d'analyseurs syntaxique bien connus : Flex pour créer un analyseur lexical, et Bison pour créer un analyseur syntaxique (il est possible que vous + les trouviez sous les noms de Lex et Yacc). L'entrée de Flex est un fichier contenu les définitions d'expressions + régulières des tokens. L'entrée de Bison est l'ensemble des règles de syntax du langage, au format BNF. +
++ Le rôle de l'analyseur syntaxique HTML est d'analyser les balises HTML sous forme d'arbre d'analyse. +
++ Le vocabulaire et la syntaxe du HTML sont définis dans les specifications créées par + l'organisation W3C. +
++ Comme nous l'avons vu dans l'introduction sur l'analyse syntaxique, la syntaxe de la grammaire peut être définie + formellement en utilisant des formats comme la BNF. +
++ Malheureusement tous les sujets d'un analyseur conventionnel ne s'appliquent pas au HTML (je ne les ai pas mentionné + juste pour le plaisir–ils seront utilisés dans l'analyse syntaxique du CSS et du JavaScript). HTML ne peut pas + être facilement défini avec la grammaire non contextuelle dont les analyseurs syntaxiques ont besoin. +
++ Il existe un format formel pour définit l'HTML–la DTD (Définition de Typ de Document)–mais il ne s'agit + pas d'une grammaire non contextuelle. +
++ Cela semble étrange au premier abord ; le HTML est plutôt proche du XML. Il existe beaucoup d'analyseurs syntaxique + pour XML. Il existe une variation XML du HTML–XHTML–alors quelle est la grosse différence ? +
++ La différence est que l'approche du HTML est plus "permissive" : elle vous laisse omettre certains tags (qui sont + alors ajoutés implicitement), ou parfois omettre des tags ouvrants ou fermants, etc. Dans l'ensemble c'est une + syntaxe "gentille", contrairement à la syntaxe rigide et exigeante du XML. +
++ Ce détail en apparence anodin change en fait tout. D'un côté c'est la principale raison à la popularité de HTML : il + vous pardonne vos erreurs et facilite la vie du créateur de contenu web. D'un autre côté, il rend difficile d'écrire + une grammaire formelle. Donc pour résumer, HTML ne peut être facilement analysé syntaxiquement en utilisant des + analyseurs syntaxiques conventionnels, parce que sa grammaire n'est pas non contextuelle. HTML ne peut être analysé + syntaxiquement par des analyseurs syntaxiques XML. +
++ La définition de HTML est au format DTD. Ce format est utilisé pour définir des langages 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 auparavant, la + DTD HTML ne forme pas une grammaire non contextuelle. +
++ Il existe quelques variations de la DTD. Le mode strict se conforme uniquement aux spécifications mais d'autres + modes contiennent un support de balises utilisées par des navigateurs dans le passé. Le but est la compatibilité + ascendante avec des contenus plus anciens. La DTD stricte actuelle est ici : www.w3.org/TR/html4/strict.dtd +
+
+ L'arbre de sortie (l'"arbre d'analyse") est un arbre d'attributs et éléments DOM. DOM est l'abbréviation de Modèle Objet de Document. C'est la représentation
+ objet du document HTML et l'interface des éléments HTML avec le monde extérieur comme JavaScript.
La racine de
+ l'arbre est l'objet "Document".
+
+ Le DOM a presque une relation un-pour-un avec les balises. Par exemple : +
+<html> + <body> + <p> + Hello World + </p> + <div> <img src="example.png"/></div> + </body> +</html> ++ Ces balises seraient traduites en l'arbre DOM suivant : + +
+ Comme HTML, le DOM est spécifié par l'organisation W3C. Voir www.w3.org/DOM/DOMTR. + Il s'agit d'une spécification générique pour manipuler les documents. Un module spécifique décrit les éléments HTML + en particulier. Les définitions HTML peuvent être trouvées 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 construit avec des éléments qui + implémentent une des interfaces du DOM. Les navigateurs utilisent des implémentations concrètes qui ont d'autres + attributs utilisés par le navigateur en interne. +
++ Comme nous l'avons vu dans les sections précédentes, HTML ne peut être analysé syntaxiquement en utilisant les + analyseurs syntaxiques normaux descendants ou ascendants. +
++ Les raisons sont : +
document.write()
)
+ peut ajouter de nouveaux tokens, et le processus d'analyse syntaxique modifie donc en fait l'entrée.+ Ne pouvant utiliser les techniques d'analyse syntaxique normales, les navigateurs créent des analyseurs syntaxiques + spécifiques pour l'analyse du HTML. +
++ L'algorithme d'analyse syntaxique + est décrit en détail par la spécification HTML5. L'algorithme consiste en deux étapes : la tokenisation et la + construction d'arbre. +
+ La tokenisation est l'analyse lexicale, qui analyse l'entrée sous forme de tokens. Parmi les tokens HTML se trouvent + des balises ouvrantes, fermantes, des noms et valeurs d'attributs. +
++ Le tokeniser reconnait le token, le donne au constructeur d'arbre, et consomme le prochain caractère pour + reconnaître le prochain token, et ainsi de suite jusqu'à la fin de l'entrée. +
+ ++ La sortie de l'algorithme est un token HTML. L'algorithme est exprimé comme une machine à état. 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 de tokenisation et par l'état de construction de l'arbre. Cela signifie que le + même caractère consommé donnera des résultats différents pour le prochain état correct, en fonction de l'état en + cours. L'algorithme est trop complexe pour être complètement décrit, donc voyons un exemple simple qui nous aidera à + comprendre le principe. + +
++ + Exemple de base–tokeniser le HTML suivant : + +
+ ++<html> + <body> + Hello world + </body> +</html> ++ +
+
+ L'état initial est l'"état donnée". Lorsque le caractère <
est rencontré, l'état est changé en "état
+ balise ouverte". Consommer un caractère a-z
provoque la création d'un "Token de début de balise",
+ et l'état est changé en "état nom de tag". Nous restons dans cet état jusqu'à ce que le caractère
+ >
soit consommé. Chaque caractère est ajouté au nouveau nom de token. Dans notre cas le token créé
+ est un token html
.
+
+
+
+ Lorsque la balise >
est atteinte, le token en cours est émis et l'état revient à l'"état
+ donnée". La balise <body>
sera traitée au travers des mêmes étapes. Jusqu'ici les balises
+ html
et body
ont été émises. Nous sommes maintenant de retour dans l'"état donnée".
+ Consommer le caractère H
de Hello world
provoquera la création et l'émission d'un token de
+ caractère, ceci jusqu'à ce que le <
de </body>
soit atteint. Nous émettrons un
+ token caractère pour chaque caractère de Hello world
.
+
+
+
+ Nous sommes maintenant revenus dans l'"état balise ouverte". Consommer la prochain entrée /
+ provoquera la création d'un token de balise fermante
et un déplacement vers l'"état nom de
+ balise". À nouveau nous restons dans cet état jusqu'à atteindre >
. Puis le nouveau token de
+ balise sera émis et nous retournerons à l'"état donnée". L'entrée </html>
sera traitée
+ comme le cas précédent.
+
+
+ Lorsque l'analyseur syntaxique est créé l'objet Document est créé. Lors de la phase de construction d'arbre l'arbre + DOM avec le Document à sa racine sera modifié et des éléments y seront ajoutés. Chaque nœud émis par le tokeniser + sera traité par le constructeur d'arbre. Pour chaque token la spécification définit quel élément du DOM est adapté + et sera créé pour ce token. L'élément est ajouté à l'arbre DOM, ainsi qu'à la pile des éléments ouverts. Cette pile + est utilisée pour corriger les incohérences d'imbrication et les balises non fermées. L'algorithme est également + décrit comme une machine à état. Les états sont appelés "nœuds d'insertion". +
+ ++ Voyons le processus de construction d'arbre pour l'entrée d'exemple : +
+ + ++<html> + <body> + Hello world + </body> +</html> ++ +
+ L'entrée de la construction d'arbre est une séquence de tokens issue d'étape de tokenisation. Le premier mode est le + "mode initial". Recevoir le token "html" provoquera un passage dans le mode "avant html" et un + retraitement du token dans ce mode. Cela provoquera la création de l'élément HTMLHtmlElement, qui sera ajouté à + l'objet racine Document. + +
++ + L'état sera changé en "avant en-tête". Le token "body" est alors reçu. Un HTMLHeadElement sera créé + implicitement bien que nous n'ayons pas de token "head" et sera ajouté à l'arbre. + +
++ + Nous passons maintenant en mode "dans l'en-tête" puis dans "après en-tête". Le token body est + retraité, un HTMLBodyElement est créé et inséré et le mode est changé à to "dans corps de document". + +
++ + Les tokens caractères de la chaîne "Hello world" sont maintenant reçus. Le premier provoquera la création et + l'insertion d'un nœud "Texte" et les autres caractères seront ajoutés à ce nœud. + +
++ + La réception du token de fin de body provoquera un passage dans le mode "après corps de document". Nous + recevrons maintenant la balise de fin de html qui nous placera dans le mode "après après corps de document". + Recevoir le token de fin de fichier terminera l'analyse syntaxique. +
+ ++ À cette étape le navigateur marquera le document comme interactif et commencera à analyser les scripts qui sont en + mode "deferred" : ceux qui doivent être exécutés après l'analyse du document. L'état du document sera alors fixé à + "complet" et un événement "load" va être émis. +
+ ++ Vous pouvez voir les algorithmes complets de + tokenisation et construction d'arbre dans la spécification HTML5 +
+ ++ Vous n'aurez jamais d'erreur "Syntaxe invalide" dans une page HTML. Les navigateurs corrigent tout contenu invalide + et poursuivent leur analyse. + +
++ Prenez ce HTML par exemple : + +
+ ++<html> + <mytag> + </mytag> + <div> + <p> + </div> + Really lousy HTML + </p> +</html> ++ +
+ + Je dois avoir violé près d'un million de règles ("mytag" n'est pas une balise standard, mauvaise imbrication des + éléments "p" et "div", etc.) mais le navigateur l'affiche toujours correctement et ne se plaint de rien. Donc une + bonne partie du code de l'analyseur syntaxique consiste à corriger les erreurs de l'auteur du code HTML. + +
++ + La gestion d'erreurs est relativement uniforme dans les navigateurs, mais assez incroyablement elle n'a pas été + incluse dans les spécifications HTML. Tout comme les signets et les boutons retour/avant, c'est juste quelque chose + qui s'est développé dans les navigateurs au fil des années. Il y a des constructions HTML invalides qui sont + répétées dans de nombreux sites, et les navigateurs tentent de les corriger d'une manière similaire à ce que font + d'autres navigateurs. + +
+ ++ La spécification HTML5 définit certaines de ces attentes (WebKit résume bien cela dans le commentaire au début de la + classe de l'analyseur syntaxique HTML). +
+ +++ +L'analyseur syntaxique analyse l'entrée tokenisée dans le document, construisant l'arbre du document. Si le + document est bien formé, son analyse est rapide.
+ +Malheureusement, nous devons supporter de nombreux documents HTML qui ne sont pas bien formés, et l'analyseur + syntaxique doit donc être tolérant aux erreurs.
+ +Nous devons prêter attention au moins aux conditions d'erreur suivantes :
+ ++
+ +- L'élément que l'on tente d'ajouter est explicitement interdit à l'intérieur d'une balise parente. Dans ce cas + nous devrions fermer toutes les balises jusqu'à celle qui interdit l'élément, et l'ajouter à la suite.
+- Nous n'avons pas le droit d'ajouter l'élément directement. Il se pourrait que la personnes rédigeant le + document a oublié une balise au milieu (ou que la balise au milieu est optionnelle). Ce pourrait être le cas + avec les balises suivantes : HTML HEAD BODY TBODY TR TD LI (en ai-je oublié ?).
+- Nous voulons ajouter un élément de type bloc dans un élément inline. Ferme tous les éléments inline jusqu'au + prochain élément bloc de plus haut niveau.
+- Si ça n'aide pas, fermer les éléments jusqu'à ce qu'il soit autorisé d'ajouter l'élément–ou ignorer la + balise.
+
+ Voyons quelques exemples de tolérance aux erreurs de WebKit : +
+
+ Certains sites utilisent </br> au lieu de <br>. Afin d'être compatible avec IE et Firefox, WebKit traite
+ cela comme <br>.
Le code :
+
+if (t->isCloseTag(brTag) && m_document->inCompatMode()) { + reportError(MalformedBRError); + t->beginTag = true; +} ++ Notez que la gestion d'erreur est interne : elle ne sera pas présentée à l'utilisateur.
+ Un tableau égaré est un tableau dans un autre tableau, mais pas à l'intérieur d'une cellule de tableau. +
+Par exemple :
++<table> + <table> + <tr><td>tableau interne</td></tr> + </table> + <tr><td>tableau externe</td></tr> +</table> ++ WebKit changera cette hiérarchie en deux tableaux voisins : +
+<table> + <tr><td>tableau externe</td></tr> +</table> +<table> + <tr><td>tableau interne</td></tr> +</table> ++ Le code : +
+if (m_inStrayTableContent && localName == tableTag) + popBlock(tableTag); ++ WebKit utilise une pile pour le contenu de l'élément courant : il dépilera le tableau interne hors de la pile du + tableau externe. Les tableaux seront alors frères.
+ Au cas où l'utilisateur place un formulaire dans un autre formulaire, le second formulaire est ignoré.
Le code
+ :
+
+if (!m_currentFormElement) { + m_currentFormElement = new HTMLFormElement(formTag, m_document); +} ++
+ 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; +} ++
+ Encore une fois–le commentaire parle de lui-même. + +
+ Support de HTML vraiment cassé. Nous ne fermons jamais la balise body, car certaines pages web stupides la ferment + avant la réelle fin du doc. Fions-nous à l'appel à end() pour fermer les choses. ++ +
+if (t->tagName == htmlTag || t->tagName == bodyTag ) + return; + ++ Donc attentions auteurs du web–à moins que vous ne vouliez apparaître comme exemple dans un extrait de code de + tolérance aux erreurs de WebKit–rédigez du HTML bien formé.
+
+ Vous vous souvenez des concepts d'analyse syntaxique dans l'introduction ? Eh bien, contrairement à HTML, CSS est + une grammaire non contextuelle et peut être analysé en utilisant les types d'analyseurs décrits dans l'introduction. + En fait les spécification CSS définissent la grammaire lexicale et + syntaxique de CSS. +
+
+ Voyons quelques exemples :
La grammaire lexicale (le vocabulaire) est définie par les expressions régulières de
+ chaque token :
+
+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}* ++
+ "ident" est l'abréviation de identificateur, comme un nom de classe par exemple. "name" est un identifiant d'élément + (référencé par "#" ) +
++ La grammaire syntaxique est décrite en 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 cette structure : +
+div.error, a.error { + color:red; + font-weight:bold; +} ++ 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. 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 certain nombre de sélecteurs séparés par + une virgule et des espaces (S veut dire espace). Un ensemble de règles contient des accolades et à l'intérieur de + celles-ci une déclaration ou éventuellement une série de déclarations séparées par un point-virgule. "declaration" et + "selector" seront définis dans les définitions BNF suivantes. +
+ WebKit utilise les générateurs d'analyseurs syntaxiques Flex et Bison pour créer + des parseurs automatiquement à partir de fichiers de grammaire CSS. Comme vous vous en souvenez lors de + l'introduction à l'analyse syntaxique, Bison crée un analyseur syntaxique ascendant de type lecture-réduction. + Firefox utilise un analyseur syntaxique descendant écrit à la main. Dans les deux cas chaque fichier CSS est analysé + pour produire un objet StyleSheet. Chaque objet contient des règles CSS. Les objets de type règle CSS contiennent un + sélecteur et des objets de déclarations ainsi que d'autres objets correspondant à la grammaire CSS. +
+ ++ 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 syntaxique trouve une balise <script>. L'analyse syntaxique du document + s'interrompt jusqu'à ce que le script ait été exécuté. Si le script est externe alors la ressource doit d'abord être + téléchargée depuis le réseau–ce qui est aussi fait de manière synchrone, et l'analyse s'interrompt jusqu'à ce + que la ressource soit récupérée. Ce fut le modèle pendant de nombreuses années et c'est aussi indiqué dans les + spécifications HTML4 et 5. Les auteurs peuvent ajouter l'attribut "defer" à un script, auquel cas il n'interrompra + pas l'analyse du document et s'exécutera après que le document ait été analysé. HTML5 ajoute une option pour marquer + le script comme asynchrone afin qu'il soit analysé et exécuté par un thread différent. +
++ WebKit come Firefox font cette optimisation. Lorsqu'ils exécutent des scripts, un autre thread analyse le reste du + document et cherche quelles autres ressources doivent être chargées du réseau et les charge. De cette manière, les + ressources peuvent être chargées sur des connexions parallèles et la vitesse globale améliorée. Note : l'analyseur + spéculatif n'analyse que les références à des ressources externes comme les feuilles de style, les images et les + scripts externes : il ne modifie pas l'arbre DOM–cela reste le travail de l'analyseur principal. +
++ Les feuilles de style d'un autre côté ont un modèle différent. Conceptuellement il semble que puisque les feuilles + de style ne changent pas l'arbre DOM, il n'y a aucune raison d'interrompre l'analyse du document pour les attendre. + Il y a la question, cependant, des scripts demandant des informations sur les styles pendant la phase d'analyse du + document. Si le style n'est pas encore chargé et analysé, le script récupèrera des réponses incorrectes et cela a + apparemment causé beaucoup de problèmes. Cela semble être un cas rare mais c'est en fait assez courant. Firefox + bloque tous les scripts lorsqu'une feuille de style est en cours de chargement et d'analyse. WebKit ne bloque les + scripts que lorsqu'ils tentent de lire certaines propriétés de style qui pourraient être affectées par des feuilles + de style non chargées. +
++ Pendant que l'arbre DOM est construit, la navigateur construit un autre arbre, l'arbre de rendu. Cet arbre est + constitué d'éléments visuels, dans l'ordre où ils seront affichés. C'est la représentation visuelle du document. Le + but de cet arbre est de permettre de dessiner les contenus dans leur ordre correct. +
+
+ Firefox appelle "frames" les éléments de l'arbre de rendu. WebKit utilise le terme de rendu ou objet de rendu.
+ Un rendu sait comment s'agencer et se dessiner lui et ses éléments fils.
La classe RenderObject de WebKit, la
+ base de tous les rendus, a la définition suivante :
+
+class RenderObject{ + virtual void layout(); + virtual void paint(PaintInfo); + virtual void rect repaintRect(); + Node* node; //the DOM node + RenderStyle* style; // le style calculé + RenderLayer* containgLayer; //the containing z-index layer +} ++
+ Chaque rendu représente une zone rectangulaire correspondant généralement à la boîte CSS d'un nœud, comme c'est décrit
+ dans la spec CSS2. Cela inclut des informations géometriques comme la largeur, la hauteur et la position.
Le type
+ de boîte dépend de la valeur "display" de l'attribut de style associé au nœud (voir la section de calcul du style). Voici le code de WebKit pour décider quel type de rendu devrait être
+ créé 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 de l'élément est aussi pris en compte : par exemple, les éléments de formulaires et les tableaux ont des + images spécifiques.
+ createRenderer()
. Les rendus pointent vers des objets de style contenant des informations non géométriques.
+
+ Il existe des éléments du DOM correspondant à plusieurs objets visuels. Ce sont généralement des éléments ayant une
+ structure complexe qui ne peut être décrite par un simple rectangle. Par exemple, l'élément "select" a 3 rendus : un
+ pour la zone d'affichage, un pour la boîte de la liste déroulante et un pour le bouton. Également lorsque le texte est
+ divisé en plusieurs lignes parce que la largeur n'est pas suffisante pour une ligne, les nouvelles lignes seront
+ ajoutées comme autant de rendus supplémentaires.
Un autre exemple de rendus multiples et le HTML cassé. Selon la
+ spéc CSS un élément inline doit contenir soit uniquement des éléments de type bloc, soit uniquement des éléments
+ inline. Dans le cas d'un contenu mixte, des rendus de bloc anonymes seront créés pour encapsuler les éléments inline.
+
+ Des objets de rendu correspondent à un nœud DOM mais par au même endroit dans l'arbre. Les éléments flottants ou + positionnés de manière absolue sont hors du flux, placés dans un endroit différent de l'arbre, et associés à l'image + réelle. Une image factice est l'endroit où elles auraient dû être. +
+ + + +
+ Dans Firefox, la présentation est abonnée aux mises-à-jour du DOM. La présentation la création d'images au
+ FrameConstructor
et le constructeur résoud le style (voir calcul du style) et crée une
+ image.
+
+ Dans WebKit le processus de résolution du style et de création d'un rendu est appelé "attachement". Chaque nœud DOM + dispose d'une méthode "attach". L'attachement est synchrone, l'insertion de nœud dans l'arbre DOM appelle la méthode + "attach" du nouveau nœud. +
+ Traiter les balises html et body débouche sur la construction de la racine de l'arbre de rendu. L'objet de rendu
+ racine correspond à ce que la spéc CSS appelle le bloc conteneur : le bloc de plus haut niveau qui contient tous les
+ autres blocs. Ses dimensions sont celles du viewport : les dimensions de la fenêtre d'affichage du navigateur.
+ Firefox l'appelle ViewPortFrame
et WebKit RenderView
. Il s'agit de l'objet de rendu vers
+ lequel pointe le document. Le reste de l'arbre est construit au travers de l'insertion de nœuds DOM.
+
+ Voir la spéc CSS2 sur le modèle de traitement. +
+ + ++ Construire l'arbre de rendu nécessite de calcul les propriétés visuelles de chaque objet de rendu. Cela est fait en + calculant les propriétés de style de chaque élément. +
++ Le style inclut les feuilles de style d'origines diverses, les éléments de style inclus et les propriétés visuelles + du HTML (comme la propriété "bgcolor"). Ces dernières sont traduites sous forme de propriétés de style CSS + correspondantes. +
++ 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–ce sont des feuilles de style + fournies par l'utilisateur du navigateur (les navigateurs vous permettent de définir vos styles favoris. Dans + Firefox, par exemple, cela est fait en plaçant une feuille de style dans le dossier "Firefox Profile"). +
++ Le calcul de style amène plusieurs difficultés : +
Trouver les règles correspondantes à chaque élément peut poser des problèmes de performance + si ce n'est pas optimisé. Traverser toute la liste de règles pour chaque élément afin de trouver des + correspondances est une tâche considérable. Les sélecteurs peuvent avoir une structure complexe pouvant amener + le processus de correspondance à emprunter un chemin prometteur qui se révèle finalement non pertinent et un + autre chemin doit alors être essayé. +
+Par exemple–ce sélecteur composite :
+ ++div div div div{ + ... +} ++ signifie que les règles s'appliquent à une
<div>
qui descend de 3 divs. Supposez que vous
+ vouliez vérifier si la règle s'applique pour un élément <div>
donné. Vous choisissez un certain
+ chemin dans l'arbre à vérifier. Vous pourriez avoir besoin de remonter l'arbre de nœuds juste pour vous apercevoir
+ qu'il n'y a que deux divs et que la règle ne s'applique donc pas. Vous devez alors essayer d'autres chemins dans
+ l'arbre.
+ + 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 frères ou cousins et : +
+ Firefox dispose de deux arbres supplémentaires pour faciliter le calcul de style : l'arbre de 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, ce sont seulement les nœuds DOM qui pointent vers le style qui leur est associé. + +
+ ++ Les contextes de style contiennent des valeurs finales. Les valeurs sont calculées en appliquant l'ensemble des + règles correspondantes dans le bon ordre et en effectuant des manipulations les transformant de valeurs logiques en + valeurs concretes. Par exemple, si la valeur logique est un pourcentage de l'écran, elle sera calculée et + transformée en unités absolues. L'idée de l'arbre de règles est vraiment maline. Elle permet de partager ces valeurs + entre nœuds pour éviter de les recalculer. Cela économise aussi de la mémoire. +
+ ++ Toutes les règles que l'on a pu faire correspondre sont stockées dans un arbre. Les nœuds du bas dans un arbre ont + une plus grande priorité. L'arbre contient tous les chemins des règles pour lesquelles on a trouvé des + correspondances. Le stockage des règles est fait à la demande. L'arbre n'est pas calculé au début pour chaque nœud + mais, à chaque fois qu'un nœud à besoin d'être calculé, les chemins calculés sont ajoutés à l'arbre. +
+
+ L'idée est de voir les chemins de l'arbre comme les mots d'un lexique. Disons que nous avons déjà calculé cet arbre
+ de règles :
+
+
+
+ Voyons comment l'arbre nous économise du travail. +
++ Les contextes de style sont divisés en structures. Ces structures contiennent des informations de style pour une + certaine catégorie comme border ou color. 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 qui, à moins d'être définies par l'élément, sont héritées du parent. Les propriétés + non héritées (appelées propriétés "reset") utilisent des valeurs par défaut si elles ne sont pas définies. +
++ L'arbre nous aide pour cacher des structures entières (contenant les valeurs calculées finales) dans l'arbre. L'idée + est que si le nœud du bas n'a pas fourni de définition pour une structure, une structure cachée dans un nœud plus + haut peut être utilisée. +
+
+ Lorsque l'on calcule le contexte de style pour un élément donné, on commence par calculer un chemin dans l'arbre de
+ règles ou en utiliser un existant. On commence alors à appliquer les règles trouvées dans le chemin pour remplir les
+ structures de notre nouveau context de style. On commence au nœud du bas du chemin–celui avec la priorité la
+ plus élevée (le sélecteur le plus spécifique généralement) et on traverse l'arbre jusqu'à ce que notre structure
+ soit complète. S'il n'y a aucune spécification pour la structure dans ce nœud de règle, alors on peut beaucoup
+ optimizer–on remonte l'arbre jusqu'à trouver un nœud qui le spécifie entièrement et on pointe simplement
+ dessus–c'est la meilleure optimisation–la structure entière est partagée. Cela évite le calcul de
+ valeurs finales et économise de la mémoire.
Si nous trouvons des définitions partielles nous remontons l'arbre
+ jusqu'à ce que la structure soit remplie.
+
+ Si nous n'avons trouvé aucune définition pour notre structure alors, au cas où la structure est un type "hérité", + nous pointons vers la structure de notre parent dans l'arbre de contexte. Dans un tel cas nous avons encore + réussi à partager les structures. Si c'est une structure de reset alors les valeurs par défaut seront utilisées. +
++ Si le nœud le plus spécifique ajoute des valeurs alors il nous faut faire des calculs supplémentaires pour les + transformer en valeurs réelles. On cache alors le résultat dans le nœud de l'arbre afin qu'il puisse être réutilisé + par des nœuds enfants. +
++ Dans le cas où un élément a un voisin ou un frère qui pointe vers le même nœud d'arbre, alors l'ensemble du + contexte de style peut être partagé entre eux. +
++ Voyons un exemple : Supposez que nous ayons ce HTML + + +
+<html> + <body> + <div class="err" id="div1"> + <p> + this is a <span class="big"> grosse erreur </span> + this is also a + <span class="big"> très grosse erreur</span> error + </p> + </div> + <div class="err" id="div2">une autre erreur</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 les choses disons que nous avons besoin de ne remplir que deux structures : la structure de couleur
+ et la structure de marge. La structure de couleur ne contient qu'un membre : la couleur. La structure de marge
+ contient les quatre côtés.
L'arbre de règles résultant ressemblera à ceci (les nœuds sont marqués avec le nom
+ du nœud : le numéro de la règle vers laquelle ils pointent):
+
+
+
+
L'arbre de contexte ressemblera à ceci (nom de nœud : le nœud de règle vers lequel ils pointent):
+
+
+
+
+ Supposons que nous analysons syntaxiquement le HTML et arrivons à la seconde balise <div>. Nous avons besoin
+ de créer un contexte de style pour ce nœud et de remplir ses structures de style.
Nous allons chercher les
+ correspondances de règles et découvrir que les règles correspondant à la <div> sont 1, 2 et 6. Cela signifie
+ qu'il existe déjà un chemin dans l'arbre que notre élément peut utiliser et que nous avons juste besoin d'y ajouter
+ un nouveau nœud pour la règle 6 (nœud F dans l'arbre de règles).
Nous créerons un contexte de style et le
+ placerons dans l'arbre de contexte. Le nouveau contexte de style pointera vers le nœud F dans l'arbre de règles.
+
+ Nous avons maintenant besoin de remplir les structures de style. Nous commencerons par remplir la structure de + marge. Puisque le dernier nœud de règle (F) n'ajoute rien à la structure de marge, nous pouvons remonter dans + l'arbre jusqu'à trouver une structure cachée, calculée lors d'une insertion précédente, et l'utiliser. Nous allons + la trouver sur le nœud B, qui est le nœud le plus élevé a avoir spécifié des règles sur les marges. +
++ Nous avons bien une définition pour la structure de couleur, et ne pouvons donc pas utiliser une structure déjà + cachée. La couleur ayant un attribut, nous n'avons pas besoin de remonter l'arbre pour remplir d'autres attributs. + Nous calculerons la valeur finale (convertir la chaîne en RVB etc) et cacherons la structure calculée sur ce nœud. +
++ Le travail sur le second élément <span> est encore plus simple. Nous chercherons les correspondances avec les + règles et en arriverons à la conclusion qu'il pointe vers la règle G, comme le span précédent. Comme nous avons des + noeuds frères qui pointent vers le même noeud, nous pouvons partager l'ensemble du contexte de style et simplement + pointer vers le contexte du span précédent. +
+
+ Pour les structures contenant des règles héritées du parent, le cache est fait sur l'arbre de context (la propriété
+ de couleur est en réalité héritée, mais Firefox la taite comme une propriété reset et la cache sur l'arbre de
+ règles).
Par exemple nous avons ajouté des règles pour les fontes dans un paragraphe :
+
+p {font-family: Verdana; font size: 10px; font-weight: bold} ++ L'élément paragraphe, qui est un élément fils de la div dans l'arbre de context, pourrait alors partager la même + structure de fonte que son élément parent. C'est le cas si aucune règle de fonte n'a été spécifié pour le + paragraphe.
+ Dans WebKit, il n'y a pas d'arbre de règles. Les déclarations qui trouvent une correspondances sont traversées quatre
+ fois. En tout premier, les règles de haute priorité non-importantes sont appliquées (les propriétés qui devraient être
+ appliquées en premier parce que d'autres en dépendent, comme display), puis celles de haute priorité importantes, puis
+ celles de priorité normales non-importantes, et enfin celles de priorité normale importantes. Cela veut dire que les
+ propriétés qui apparaissent plusieurs fois seront résolues dans le bon ordre de cascade. Le dernier gagne.
+
+ Donc pour résumer : partager les objets de style (entièrement ou certaines des structures qu'ils contiennent) permet + d'adresser les problèmes 1 et 3. L'arbre de règles de Firefox aide + également à appliquer les propriétés dans le bon ordre. +
++ Il existe plusieurs sources de règles de style : +
+p {color: blue} ++
+<p style="color: blue" /> ++
+<p bgcolor="blue" /> ++
+ Les deux dernières sont facilement associées à l'élément puisque les attributs de style et que les attributs HTML + peuvent être associés en utilisant l'élément comme clé. +
++ Comme indiqué précédemment pour le problème n°2, la correspondance de règle CSS peut être plus + complexe. Pour résoudre la difficulté, les règles sont manipulées pour un accès plus facile. +
+
+ Après avoir analysé syntaxiquement la feuille de style, les règles sont ajoutées à une des diverses tables de
+ hachage, en fonction du sélecteur. Il y a plusieurs maps par id, par nom de classe, par nom de balise et une map
+ générale pour tout ce qui ne rentre pas dans ces catégories. Si le sélecteur est un id, la règle sera ajoutée à la
+ map des ids, si c'est une classe elle sera ajoutée à la map des classes, etc.
Cette manipulation facilite
+ beaucoup la recherche de correspondance de règles. Il n'y a pas besoin de rechercher dans chaque déclaration : on
+ peut extraire de ces maps les règles à associer à un élément. Cette optimisation élimine 95+% des règles, de sorte
+ qu'il n'y a même plus besoin de les prendre en compte durant le process de recherche de correspondances (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 la map de classes. La seconde dans la map des ids et la troisième dans la map des + balises.
+<p class="error">une erreur est intervenue </p> +<div id=" messageDiv">ceci est un message</div> ++
+ Nous allons d'abord essayer de trouver des règles pour l'élément p. La map de classes contiendra une clé "error" avec
+ laquelle on pourra trouver la règle pour "p.error". L'élément div aura des règles associées dans la map des ids (la
+ clé est l'id) et la map de balises. Le seul travail restant est donc de trouver quelles règles parmi celles extraites
+ correspond vraiment.
Par exemple si la règle pour la div était
+
+table div {margin: 5px} ++ elle sera quand même extraite de la map des balises, parce que la clé est le sélecteur le plus à droite, mais elle ne + sera pas associée à notre élément div, qui n'a pas d'ancêtre de type table.
+ WebKit comme Firefox effectuent cette manipulation. +
++ L'objet de style a des propriétés correspondant à chaque attribut visuel (tous les attributs CSS, mais en plus + générique). Si la propriété n'est définie par aucune des règles trouvées, alors des propriétés peuvent être héritées + via l'objet de style de l'élément parent. Les autres propriétés ont des valeurs par défaut. +
++ Le problème survient lorsqu'il y a plus d'une définition–arrive alors l'ordre de la cascade pour résoudre le + problème. +
+ Les déclarations du navigateur sont moins importantes et l'utilisateur ne passe devant l'auteur que lorsque ses + déclarations sont marquées comme importantes. Les déclarations du même ordre seront triées par spécificité puis en fonction de l'ordre dans lequel elles sont spécifiées. Les attributs + visuels HTML sont traduits sous forme de déclarations CSS. Ils sont traités comme des règles de l'auteur avec une + faible priorité. +
++ La spécificité du sélecteur est définie par la spécification CSS2 comme suit : +
+ La base numérique que vous devez utiliser est définie par le plus grand nombre que vous avez dans une des catégories.
+
Par exemple, si a=14 vous devez utiliser une base hexadécimale. Dans le cas peu probable où a=17 vous devrez
+ utiliser une base numérique à 17 chiffres. Cette dernière situation peut arriver avec un sélecteur comme ceci : html
+ body div div p ... (17 balises dans votre sélecteur.. peu probable).
+
+ Quelques exemples : +
+ * {} /* a=0 b=0 c=0 d=0 -> spécificité = 0,0,0,0 */ + li {} /* a=0 b=0 c=0 d=1 -> spécificité = 0,0,0,1 */ + li:first-line {} /* a=0 b=0 c=0 d=2 -> spécificité = 0,0,0,2 */ + ul li {} /* a=0 b=0 c=0 d=2 -> spécificité = 0,0,0,2 */ + ul ol+li {} /* a=0 b=0 c=0 d=3 -> spécificité = 0,0,0,3 */ + h1 + *[rel=up]{} /* a=0 b=0 c=1 d=1 -> spécificité = 0,0,1,1 */ + ul ol li.red {} /* a=0 b=0 c=1 d=3 -> spécificité = 0,0,1,3 */ + li.red.level {} /* a=0 b=0 c=2 d=1 -> spécificité = 0,0,2,1 */ + #x34y {} /* a=0 b=1 c=0 d=0 -> spécificité = 0,1,0,0 */ + style="" /* a=1 b=0 c=0 d=0 -> spécificité = 1,0,0,0 */ ++ +
+ Une fois les règles trouvées, elles sont triées selon les règles de la cascade. WebKit utilise un tri à bulles pour + les petites listes et un tri fusion pour les grandes. WebKit implémente le tri en redéfinissant 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; +} ++ +
+ WebKit utilise un flag pour marquer si toutes les feuilles de style de plus haut niveau (y compris les @imports) ont + été chargées. Si le style n'est pas complètement chargé lors de l'attachement, des espaces réservés sont utilisés et + cela est marqué dans le document, et ils seront recalculés lorsque les feuilles de style auront été chargées. +
+ + ++ Lorsque le rendu est créé et ajouté à l'arbre, il n'a pas de position ni de taille. Calculer ces valeurs appelé + layout ou reflow. +
++ HTML utilise un modèle d'agencement basé sur un flux, ce qui veut dire que la plupart du temps il est possible de + calculer la géométrie en une seule fois. Les éléments arrivant plus tard ``dans le flux'' n'affecteront typiquement + pas la géométrie des éléments qui étaient auparavant ``dans le flux'', et donc le positionnement des éléments peut + se faire de gauche à droite, de haut en bas pour tout le document. Il y a des exceptions : par exemple, les tableaux + HTML peuvent demander plus d'une passe (3.5). +
++ Le système de coordonnées est relative à l'image racine. Les coordonnées haut et gauche sont utilisées. +
+
+ La mise en page est un processus récursif. Cela commence au rendu racine, qui correspond à l'élément
+ <html>
du document HTML. La mise en page continue récursivement à travers une partie ou l'ensemble
+ de la hiérarchie des images, en calculant les informations géométriques pour chaque rendu qui le nécessite.
+
+ Tous les rendus ont une méthode "layout" ou "reflow", et chaque rendu appelle la méthode layout de ses enfants qui + nécessitent une mise en page. +
++ Afin de ne pas procéder à une remise en page totale à chaque petit changement, les navigateurs utilisent un système + de "marqueur de changement". Un rendu qui est changé ou ajouté se marque lui et ses enfants comme "modifié" : + nécessitant une remise en page. +
++ Il existe deux marqueurs : "modifié", et "les enfants ont été modifiés", ce qui signifie que bien que le rendu + lui-même puisse être OK, au moins un de ses enfants nécessite une remise en page. +
++ La mise en page peut être déclenchée sur l'arbre de rendu en entier–il s'agit alors d'une mise en page + "globale". Cela peut arriver lorsque : +
+ La mise en page peut être incrémentale, et seuls les rendus modifiés seront repositionnés (cela peut provoquer des
+ dégâts qui demanderont d'autres remise en page).
Une mise en page incrémentale est déclenchée (de manière
+ asynchrone) lorsque les rendus sont marqués comme modifiés. Par exemple lorsque de nouveaux rendus sont ajoutés à
+ l'arbre de rendu après que du nouveau contenu soit arrivé du réseau et ajouté à l'arbre DOM.
+
+ La mise en page se fait généralement selon le schéma suivant : +
+ Firefox utilise un objet "état" (nsHTMLReflowState) comme paramètre de la mise en page (appelée "reflow"). Entre
+ autres choses cet état inclut la largeur des parents.
Le résultat de la mise en page de Firefox est un objet
+ "métriques" (nsHTMLReflowMetrics). Il contiendra la hauteur calculée du rendu.
+
+ La largeur du rendu est calculée en utilisant la largeur du bloc conteneur, la propriété "width" du style du rendu,
+ les marges et les bordures.
Par exemple la lageur de la div suivante :
+
+<div style="width: 30%"/> ++ sera calculée par WebKit comme suit (classe RenderBox, méthode calcWidth): +
+clientWidth() - paddingLeft() - paddingRight() ++ clientWidth et clientHeight représentent l'intérieur d'un objet, sans la bordure ni la barre de défilement. +
+ Les valeurs sont cachées au cas où une mise en page soit demandée, mais la largeur ne change pas. +
++
+ Lorsqu'un rendu au milieu d'une mise en page décide qu'il doit revenir à la ligne, le rendu s'arrête et propage au + parent de la mise en page qu'il a besoin de revenir à la ligne. Le parent crée des rendus supplémentaires et appelle + layout dessus. +
++ Dans l'étape de dessin, l'arbre de rendu est traversé et la méthode "paint()" du rendu est appelée pour afficher le + contenu à l'écran. Le dessin utilise le composant de l'infrastructure UI. +
+ Firefox optimise le processus en évitant d'ajouter ce qui sera caché, comme des éléments entièrement masqués + derrière d'autres éléments opaques. +
++while (!mExiting) + NS_ProcessNextEvent(thread); ++ + +
+ Selon la spécification CSS2, le terme de canvas + décrit "l'espace où est rendu la structure de formatage" : là où le navigateur dessine le contenu. + + Le canvas est infini pour chaque dimension de l'espace mais les navigateurs choisissent une largeur initiale basée + sur les dimensions du viewport. +
++ Selon www.w3.org/TR/CSS2/zindex.html, le canvas est transparent + s'il est contenu dans un autre, et reçoit une couleur définie par le navigateur s'il n'en a pas. +
+
+ Le modèle de boîte CSS décrit des boîtes rectangulaires qui sont
+ générées pour les éléments de l'arbre du document et affichées conformément au modèle de formatage visuel.
+ Chaque boîte dispose d'une zone de contenu (e.g. du texte, une image, etc.) ainsi que d'éventuelles bordure et
+ marges interne et externe.
+
+
+
+
+ Chaque noeud génère 0..n de ces boîtes.
Tous les éléments ont une propriété "display" qui détermine le type de
+ boîte qui sera générée.
+
+ Exemples :
+
+
+block : génère une boîte de type bloc. +inline : génère une ou plusieurs boîte en ligne. +none : aucune boîte n'est générée. ++ Le défaut est inline mais la feuille de style du navigateur peut fixer d'autres valeurs par défaut. Par exemple : le + display par défaut pour un élément "div" est block.
+ Il y a trois schémas : +
+ Le schéma de positionnement est fixé par la propriété "position" et l'attribut "float". +
+ La manière dont la boîte est affichée est déterminée par : +
+ Boîte bloc : forme un bloc–possède son propre rectangle dans la fenêtre du navigateur. +
+ + ++ Boîte en ligne : ne possède pas son propre bloc, mais se trouve à l'intérieur un bloc conteneur. +
+ + ++ Les blocs sont formatés verticalement l'un après l'autre. Les boîtes en lignes sont formatées horizontalement. +
+ + ++ Les boîtes en ligne sont placées dans des lignes ou "boîtes de ligne". Les lignes sont au moins aussi hautes que la + boîte la plus haute mais peuvent être plus hautes, lorsque les boîtes sont alignées "baseline"–ce qui veut + dire que la partie basse d'un élément est alignée en un point d'une autre boîte autre que sa base. Si la largeur du + conteneur n'est pas suffisante, les inlines seront plus sur plusieurs lignes. C'est généralement ce qui se passe + dans un paragraphe. + +
+ + ++ Positionnement relatif–positionné comme d'habitude plus déplacé selon le delta demandé. +
+ ++ Une boîte flottante est décalée vers la gauche ou la droite sur une ligne. La caractéristique intéressante est que + les autres boîtes la contournent. Le HTML: +
+<p> + <img style="float: right" src="images/image.gif" width="100" height="100"> + Lorem ipsum dolor sit amet, consectetuer... +</p> ++ Ressemblera à : + + +
+ La mise en page est définie exactement, indépendamment du flux normal. L'élément ne participe pas au flux normal.
+ Les dimensions sont relatives au conteneur. Dans le cas fixé, le conteneur est le viewport.
+
+
+
Note : la boîte fixée ne se déplacera pas même lors que l'on fait défiler le document !
+ Ceci est spécifié par la propriété CSS z-index. Elle représente la 3ème dimension de la boîte : sa position le long + de l'"axe z". +
+
+ Les boîtes sont réparties en piles (appelées contextes d'empilement). Dans chaque pile
+ les éléments à l'arrière seront dessinés en premier et les éléments à l'avant par-dessus, plus près de
+ l'utilisateur. Dans le cas d'un recouvrement, l'élément le plus proche masquera l'élément précédent.
Les piles
+ sont ordonnées selon la propriété z-index. Les boîtes ayant une propriété "z-index" forment une pile locale. Le
+ viewport a 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 ainsi : + + +
+ Bien que la div rouge précède la verte dans le balisage, et aurait donc dû être dessinée avant dans le flux normal, + sa propriété z-index est plus élevée, et elle est donc plus en avant dans la pile possédée par la boîte racine. +
+ +Cette page a été traduite en Japonais, deux fois ! How Browsers + Work–Behind the Scenes of Modern Web Browsers (ja) by @_kosei_ + and also ブラウザってどうやって動いてるの?(モダンWEBブラウザシーンの裏側 by @ikeike443 and @kiyoto01. + Thanks everyone!
+ + +{% endblock %}