學(xué)習(xí)內(nèi)容來自于HTML5Rocks網(wǎng)站颊亮,《瀏覽器的工作原理:現(xiàn)代瀏覽器幕后揭秘》,簡(jiǎn)單輸入輸出一下讀后筆記陨溅。
解析
解析文檔是指將文檔轉(zhuǎn)化成有意義的結(jié)構(gòu)终惑,也就是可讓代碼理解和使用的結(jié)構(gòu)。
解析得到的結(jié)果通常是代表了文檔結(jié)構(gòu)的節(jié)點(diǎn)樹门扇,它稱作解析樹或者語(yǔ)法樹雹有。
HTML解析
HTML語(yǔ)法定義
常規(guī)解析器都不適用于HTML,HTML并不能很容易地用解析器所需的的上下文無關(guān)的語(yǔ)法來定義臼寄。
有一種可以定義HTML的正規(guī)格式:DTD(Document Type Definition霸奕,文檔類型定義),但它還是與上下文無關(guān)的語(yǔ)法吉拳。原因是HTML的語(yǔ)法處理很寬容质帅,允許省略某些隱匿添加的標(biāo)記,有時(shí)還能省略一些起始或者結(jié)束標(biāo)記等等留攒。
DOM
解析器輸出的“解析樹”是由DOM元素與屬性節(jié)點(diǎn)構(gòu)成的樹結(jié)構(gòu)煤惩。DOM是文檔對(duì)象模型(Document Object Model)的縮寫。它是HTML文檔的對(duì)象表示炼邀,同時(shí)也是外部?jī)?nèi)容與HTML元素之間的接口魄揉。
HTML5規(guī)范詳細(xì)地打描述了解析算法。此算法由兩個(gè)階段組成:標(biāo)記化和樹構(gòu)建拭宁。
![HTML解析流程][fig9]
[fig9]:http://1-ps.googleusercontent.com/x/s.html5rocks-hrd.appspot.com/www.html5rocks.com/zh/tutorials/internals/howbrowserswork/308x400ximage017.png.pagespeed.ic.BGy2jYmiQr.jpg "HTML5規(guī)范中的解析流程"
解析算法
我們?cè)谥罢鹿?jié)已經(jīng)說過什猖,HTML 無法用常規(guī)的自上而下或自下而上的解析器進(jìn)行解析票彪。
原因在于:
- 語(yǔ)言的寬容本質(zhì)。
- 瀏覽器歷來對(duì)一些常見的無效 HTML 用法采取包容態(tài)度不狮。
- 解析過程需要不斷地反復(fù)。源內(nèi)容在解析過程中通常不會(huì)改變在旱,但是在 HTML 中摇零,腳本標(biāo)記如果包含 document.write,就會(huì)添加額外的標(biāo)記桶蝎,這樣解析過程實(shí)際上就更改了輸入內(nèi)容驻仅。
HTML的解析算法由兩個(gè)階段組成:標(biāo)記化和樹構(gòu)建
標(biāo)記化算法
<html>
<body>
Hello world
</body>
</html>
初始狀態(tài)是數(shù)據(jù)狀態(tài),當(dāng)遇到字符<
時(shí)登渣,狀態(tài)更改為“標(biāo)記打開狀態(tài)”噪服。接收一個(gè)a-z
字符會(huì)創(chuàng)建“起始標(biāo)記”,狀態(tài)更改為“標(biāo)記名稱狀態(tài)”胜茧。這個(gè)狀態(tài)會(huì)一直保持到接收>
粘优。在此期間接收的每個(gè)字符都會(huì)附加到新的標(biāo)記名稱上。在本例中呻顽,我們創(chuàng)建的標(biāo)記是html
標(biāo)記雹顺。
遇到>
標(biāo)記時(shí),會(huì)發(fā)送當(dāng)前的標(biāo)記廊遍,狀態(tài)發(fā)回“數(shù)據(jù)狀態(tài)”嬉愧。<body>
標(biāo)記也會(huì)進(jìn)行同樣的處理。目前html
和body
標(biāo)記均已發(fā)出『砬埃現(xiàn)在我們回到“數(shù)據(jù)狀態(tài)”没酣。接收到Hello world
中的H
字符時(shí),將創(chuàng)建并發(fā)送字符標(biāo)記卵迂,直到接收</body>
中的<
裕便。我們將為Hello world
中的每個(gè)字符都發(fā)送一個(gè)字符標(biāo)記。
現(xiàn)在我們回到“標(biāo)記打開狀態(tài)”狭握。接收下一個(gè)輸入字符/
時(shí)闪金,會(huì)創(chuàng)建end tag token
并改為“標(biāo)記名稱狀態(tài)”。我們會(huì)再次保持這個(gè)狀態(tài)论颅,直到接收>
哎垦。然后將發(fā)送新的標(biāo)記,并回到“數(shù)據(jù)狀態(tài)”恃疯。</html>
輸入也會(huì)進(jìn)行同樣的處理漏设。

樹構(gòu)建算法
樹構(gòu)建階段的輸入是一個(gè)來自標(biāo)記化階段的標(biāo)記序列。第一個(gè)模式是“initial mode”今妄。接收HTML標(biāo)記后轉(zhuǎn)為“before html”模式郑口,并在這個(gè)模式下重新處理此標(biāo)記鸳碧。這樣會(huì)創(chuàng)建一個(gè)HTMLHtmlElement元素,并獎(jiǎng)其附加到Document根對(duì)象上犬性。
后續(xù)狀態(tài):
- “before head”瞻离,接收“body”標(biāo)記,創(chuàng)建HTMLHeadElement乒裆,添加到樹中套利。
- “in head”模式,然后轉(zhuǎn)入“after head”模式鹤耍。創(chuàng)建并插入HTMLBodyElement肉迫,然后模式轉(zhuǎn)變?yōu)?strong>“body”。
- 接收body中的字符串稿黄,然后創(chuàng)建并插入“text”節(jié)點(diǎn)喊衫,其他字符也將附加到該節(jié)點(diǎn)
- 接收body結(jié)束標(biāo)記,觸發(fā)after body模式杆怕,接收剩余的HTML結(jié)束標(biāo)記族购。解析過程結(jié)束。

解析結(jié)束后的操作
文檔標(biāo)記為交互狀態(tài)财著,可以解析處于“deferred”模式的腳本联四。
瀏覽器容錯(cuò)機(jī)制
瀏覽器會(huì)糾正任何無效內(nèi)容,然后繼續(xù)工作撑教。Webkit在HTML解析器類的形狀注釋中對(duì)此做了相應(yīng)的概括:
解析器對(duì)標(biāo)記化輸入內(nèi)容進(jìn)行解析朝墩,以構(gòu)建文檔樹。如果文檔的格式正確伟姐,就直接進(jìn)行解析收苏。遺憾的是,我們不得不處理很多格式錯(cuò)誤的 HTML 文檔愤兵,所以解析器必須具備一定的容錯(cuò)性鹿霸。
我們至少要能夠處理以下錯(cuò)誤情況:
- 明顯不能在某些外部標(biāo)記中添加的元素。在此情況下秆乳,我們應(yīng)該關(guān)閉所有標(biāo)記懦鼠,直到出現(xiàn)禁止添加的元素,然后再加入該元素屹堰。
- 我們不能直接添加的元素肛冶。這很可能是網(wǎng)頁(yè)作者忘記添加了其中的一些標(biāo)記(或者其中的標(biāo)記是可選的)。這些標(biāo)簽可能包括:HTML HEAD BODY TBODY TR TD LI(還有遺漏的嗎扯键?)睦袖。
- 向 inline 元素內(nèi)添加 block 元素。關(guān)閉所有 inline 元素荣刑,直到出現(xiàn)下一個(gè)較高級(jí)的 block 元素馅笙。
- 如果這樣仍然無效伦乔,可關(guān)閉所有元素,直到可以添加元素為止董习,或者忽略該標(biāo)記烈和。
CSS解析
詞法語(yǔ)法(詞匯)是針對(duì)各個(gè)標(biāo)記用正則表達(dá)式定義的:
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}*
語(yǔ)法是采用BNF格式描述的。什么是BNF格式皿淋?豆瓣里面有解釋斥杜。
Webkit CSS 解析器

呈現(xiàn)樹構(gòu)建(Render Tree)
在 DOM 樹構(gòu)建的同時(shí),瀏覽器還會(huì)構(gòu)建另一個(gè)樹結(jié)構(gòu):呈現(xiàn)樹沥匈。這是由可視化元素按照其顯示順序而組成的樹,也是文檔的可視化表示忘渔。它的作用是讓您按照正確的順序繪制內(nèi)容高帖。
在Webkit中,如果一個(gè)元素需要?jiǎng)?chuàng)建特殊的呈現(xiàn)器畦粮,就會(huì)替換createRenderer
方法散址。呈現(xiàn)器所指向的樣式對(duì)象中包含了一些和幾何無關(guān)的信息。
呈現(xiàn)樹與DOM樹的關(guān)系

樣式計(jì)算
共享樣式數(shù)據(jù)
Webkit 節(jié)點(diǎn)會(huì)引用樣式對(duì)象 (RenderStyle)宣赔。這些對(duì)象在某些情況下可以由不同節(jié)點(diǎn)共享预麸。這些節(jié)點(diǎn)是同級(jí)關(guān)系,并且:
- 這些元素必須處于相同的鼠標(biāo)狀態(tài)(例如儒将,不允許其中一個(gè)是“:hover”狀態(tài)吏祸,而另一個(gè)不是)
- 任何元素都沒有 ID
- 標(biāo)記名稱應(yīng)匹配
- 類屬性應(yīng)匹配
- 映射屬性的集合必須是完全相同的
- 鏈接狀態(tài)必須匹配
- 焦點(diǎn)狀態(tài)必須匹配
- 任何元素都不應(yīng)受屬性選擇器的影響,這里所說的“影響”是指在選擇器中的任何位置有任何使用了屬性選擇器的選擇器匹配
- 元素中不能有任何 inline 樣式屬性
- 不能使用任何同級(jí)選擇器钩蚊。WebCore 在遇到任何同級(jí)選擇器時(shí)贡翘,只會(huì)引發(fā)一個(gè)全局開關(guān),并停用整個(gè)文檔的樣式共享(如果存在)砰逻。這包括 + 選擇器以及 :first-child 和 :last-child 等選擇器鸣驱。