瀏覽器是怎樣工作的:渲染引擎,HTML解析(連載二)

渲染引擎

渲染引擎的職責(zé)是……渲染,也就是把請(qǐng)求的內(nèi)容顯示到瀏覽器屏幕上粪小。

默認(rèn)情況下渲染引擎可以顯示HTML大磺,XML文檔以及圖片。 通過插件(瀏覽器擴(kuò)展)它可以顯示其它類型文檔探膊。比如使用PDF viewer插件顯示PDF文件杠愧。我們會(huì)在一個(gè)專門的章節(jié)討論插件與擴(kuò)展。在這一節(jié)我們將專注渲染引擎的主要用途——顯示用CSS格式化的HTML與圖片逞壁。

各種渲染引擎

我們提到的Firefox, Safari兩種瀏覽器構(gòu)建于兩種渲染引擎之上:Firefox使用Gecko —— Mozilla自家的渲染引擎流济;Safari 和 Chrome 都使用 Webkit。

Webkit 是一個(gè)開源的渲染引擎腌闯,它源自Linux平臺(tái)上的一個(gè)引擎绳瘟,經(jīng)過Apple公司的修改可以支持Mac與Windows平臺(tái)。更多信息可以參考:http://webkit.org/姿骏。

主要流程

渲染引擎開始于從網(wǎng)絡(luò)層獲取請(qǐng)求內(nèi)容糖声,一般是不超過8K的數(shù)據(jù)塊。接下來就是渲染引擎的基本工作流程:

圖 2:渲染引擎的基本工作流程(解析HTML構(gòu)建DOM樹分瘦,渲染樹構(gòu)建蘸泻,渲染樹布局,繪制渲染樹)嘲玫。

渲染引擎會(huì)解析HTML文檔并把標(biāo)簽轉(zhuǎn)換成內(nèi)容樹中的DOM節(jié)點(diǎn)悦施。它會(huì)解析style元素和外部文件中的樣式數(shù)據(jù)。樣式數(shù)據(jù)和HTML中的顯示控制將共同用來創(chuàng)建另一棵樹——渲染樹去团。

渲染樹包含帶有顏色抡诞,尺寸等顯示屬性的矩形。這些矩形的順序與顯示順序一致土陪。

渲染樹構(gòu)建完成后就是”布局“處理沐绒,也就是確定每個(gè)節(jié)點(diǎn)在屏幕上的確切顯示位置。 下一個(gè)步驟是繪制—— 遍歷渲染樹并用UI后端層將每一個(gè)節(jié)點(diǎn)繪制出來旺坠。

一定要理解這是一個(gè)緩慢的過程乔遮,為了更好的用戶體驗(yàn),渲染引擎會(huì)嘗試盡快的把內(nèi)容顯示出來取刃。它不會(huì)等到所有HTML都被解析完才創(chuàng)建并布局渲染樹蹋肮。它會(huì) 在處理后續(xù)內(nèi)容的同時(shí)把處理過的局部?jī)?nèi)容先展示出來。

主要流程示例

圖 3:Webkit主要流程

圖 4:Mozilla的Gecko渲染引擎主要流程(3.6)

從圖3和圖4中可以看出璧疗,盡管Webkit與Gecko使用略微不同的術(shù)語坯辩,這個(gè)過程還是基本相同的。

Gecko 里把格式化好的可視元素稱做“幀樹”(Frame tree)崩侠。每個(gè)元素就是一個(gè)幀(frame)漆魔。 Webkit 則使用”渲染樹”這個(gè)術(shù)語,渲染樹由”渲染對(duì)象”組成。Webkit 里使用”layout”表示元素的布局改抡,Gecko則稱為”Reflow”矢炼。Webkit使用”Attachment”來連接DOM節(jié)點(diǎn)與可視化信息以構(gòu)建渲染樹。一個(gè)非語義上的小差別是Gecko在HTML與DOM樹之間有一個(gè)附加的層 阿纤,稱作”content sink”句灌,是創(chuàng)建DOM對(duì)象的工廠。我們會(huì)討論流程中的每一部分欠拾。

解析

因?yàn)榻馕鍪卿秩疽嬷幸粋€(gè)很重要的處理胰锌,我們會(huì)講的略深入一些。讓我們從一個(gè)小的解析介紹開始藐窄。

解析一個(gè)文檔意味著把它翻譯成有意義的結(jié)構(gòu)以供代碼使用资昧。解析的結(jié)果通常是一個(gè)表征文檔的由節(jié)點(diǎn)組成的樹,稱為解析樹或句法樹荆忍。

示例——解析表達(dá)式”2 + 3 – 1″可以返回下面的樹:

圖 5:數(shù)學(xué)表達(dá)式樹節(jié)點(diǎn)

語法

解析是基于文檔所遵循的語法規(guī)則——書寫所用的語言或格式——來進(jìn)行的格带。每一種可以解析的格式必須由確定的語法與詞匯組成。這被稱之為上下文無關(guān)語法东揣。 人類語言并非此種語言践惑,所以不能用常規(guī)的解析技術(shù)來解析腹泌。

解析器——詞法分析器組合

解析器有兩個(gè)處理過程——詞法分析與句法分析嘶卧。

詞法分析負(fù)責(zé)把輸入切分成符號(hào)序列,符號(hào)是語言的詞匯——由該語言所有合法的單詞組成专甩。

句法分析是對(duì)該語言句法法則的應(yīng)用。

解析器通常把工作分給兩個(gè)組件——分詞程序負(fù)責(zé)把輸入切分成合法符號(hào)序列棺耍,解析程序負(fù)責(zé)按照句法規(guī)則分析文檔結(jié)構(gòu)和構(gòu)建句法樹。詞法分析器知道如何過濾像空格蒙袍,換行之類的無關(guān)字符害幅。

圖 6:從源文檔到解析樹(文檔,詞法分析佣赖,句法分析,解析樹)蹂午。

解析過程是交互式的。解析器通常會(huì)從詞法分析器獲取新符號(hào)并嘗試匹配句法規(guī)則晚胡。如果匹配成功,就在句法樹上創(chuàng)建相應(yīng)的節(jié)點(diǎn)遣妥,并繼續(xù)從詞法分析器獲取下一個(gè)符號(hào)谭贪。如果沒有匹配的規(guī)則慨削,解析器會(huì)內(nèi)部保存這個(gè)符號(hào),并繼續(xù)從詞法分析器獲取符號(hào)猿规,直到內(nèi)部保存的所有符號(hào)能夠成功匹配一個(gè)規(guī)則蘸拔。如果最終無法匹配,解析器會(huì)拋出異常邓萨。這意味著文檔無效,含有句法錯(cuò)誤熏兄。

轉(zhuǎn)換

多數(shù)情況下解析樹并非最終結(jié)果帽揪。解析經(jīng)常是為了從輸入文檔轉(zhuǎn)換成另外一種格式。比如編譯器要把源碼編譯成機(jī)器碼查邢,會(huì)首先解析成解析樹扶踊,再把解析樹轉(zhuǎn)換成機(jī)器碼番官。

圖 7:編譯過程(源碼,解析,解析樹懂讯,轉(zhuǎn)換,機(jī)器碼)。

解析示例

在圖5中我們構(gòu)建了一個(gè)數(shù)學(xué)表達(dá)式解析樹谨读。讓我們來試著定義一個(gè)簡(jiǎn)單的數(shù)學(xué)語言并看看解析是如何進(jìn)行的。

詞匯:我們的語言可以包含整數(shù)塑径,加號(hào)和減號(hào)。

句法:

句法塊由表達(dá)式,術(shù)語及操作符組成。

我們的語言可以包含任意數(shù)量表達(dá)式烹吵。

表達(dá)式定義為術(shù)語緊跟著操作符呀酸,再跟另外一個(gè)術(shù)語窿吩。

操作符是加號(hào)或減號(hào)。

術(shù)語可以是整數(shù)或表達(dá)式携冤。

讓我們分析輸入”2 + 3 – 1″扣猫。

第一個(gè)符合規(guī)則的子字符串是”2″癌幕,根據(jù)規(guī)則#5它是一個(gè)術(shù)語。第二個(gè)匹配是”2 + 3″,符合第二條規(guī)則——一個(gè)術(shù)語緊跟一個(gè)操作符再跟另外一個(gè)術(shù)語。下一個(gè)匹配出現(xiàn)在輸入結(jié)束時(shí)“牍粒”2 + 3 – 1″是一個(gè)表達(dá)式,因?yàn)槲覀円阎?+3”是一個(gè)術(shù)語,所以符合第二條規(guī)則。 “2 + + “不會(huì)匹配任何規(guī)則,所以是無效的輸入史煎。

詞法與句法的合法性定義

詞匯通常用正則表達(dá)式來表示酝枢。

比如我們的語言可以定義為:

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

PLUS : +

MINUS: -

如你所見袍患,整型是由正則表達(dá)式定義的滞欠。

句法常用BNF格式定義妖滔,我們的語言被定義為:

expression :=? term? operation? term

operation :=? PLUS | MINUS

term := INTEGER | expression

我們說過常規(guī)解析器只能解析上下文無關(guān)語法的語言采蚀。這種語言的一個(gè)直覺的定義是它的句法可以用BNF完整的表達(dá)亥鸠。其規(guī)范定義請(qǐng)參考http://en.wikipedia.org/wiki/Context-free_grammar

解析器的類型

解析器有兩種基本類型——自上而下解析器和自下而上解析器家妆。主觀上可以認(rèn)為自上而下的解析器從上層句法結(jié)構(gòu)開始嘗試匹配句法哨坪;自下而上的則從輸入開始彼硫,慢慢轉(zhuǎn)換成句法規(guī)則,從底層規(guī)則開始慧妄,直到上層規(guī)則全部匹配运挫。

讓我們看看這兩種解析器將怎樣解析我們的例子:

自上而下解析器從上層規(guī)則開始,它會(huì)把”2 + 3″定義為表達(dá)式翘瓮,然后定義”2 + 3 – 1″為表達(dá)式(定義表達(dá)式的過程中也會(huì)匹配其它規(guī)則每庆,但起點(diǎn)是最高級(jí)別規(guī)則)帖鸦。

自下而上的解析器會(huì)掃描輸入攻锰,直到有匹配的規(guī)則,它會(huì)把輸入替換成規(guī)則。這樣一直到輸入結(jié)束物臂。部分匹配的規(guī)則會(huì)放入解析堆棧。

StackInput

2 + 3 – 1

term+ 3 – 1

term operation3 – 1

expression– 1

expression operation1

expression

這種自下而上的解析器叫作移位歸約解析器偎巢,因?yàn)檩斎氡幌蛴乙苿?dòng)(想象一下一個(gè)指針從指向輸入開始逐漸向右移動(dòng)) 并逐漸歸約到句法樹。

自動(dòng)創(chuàng)建解析器

有一些工具可以為你創(chuàng)建解析器傲绣,它們通常稱為解析器生成器彪杉。你只需要提供語法——詞匯與句法規(guī)則——它就能生成一個(gè)可以工作的解析器渴丸。創(chuàng)建解析器需要對(duì)解析器有深入的了解,并且手動(dòng)創(chuàng)建一個(gè)優(yōu)化的解析器并不容易枢析,所以解析器生成工具很有用啊易。

Webkit使用兩款知名的解析器生成工具:Flex用于創(chuàng)建詞法分析器昼丑,Bison用于創(chuàng)建解析器 (你也許會(huì)看到它們以Lex和Yacc的名字存在)呼奢。Flex的輸入文件是符號(hào)的正則表達(dá)式定義握础,Bison的輸入文件是BNF格式的句法定義郎哭。

HTML解析器

HTML解析器的工作是解析HTML標(biāo)記到解析樹。

HTML語法定義

HTML的詞匯與句法定義在w3c組織創(chuàng)建的規(guī)范中亥至。當(dāng)前版本是HTML4衣吠,HTML5的工作正在進(jìn)行中惊搏。

不是上下文無關(guān)語法

在對(duì)解析器的介紹中看到向拆,語法可以用類似BNF的格式規(guī)范地定義浓恳。不幸的是所有常規(guī)解析器的討論都不適用于HTML(我提及它們并不是為了娛樂,它們可以用于解析CSS和JavaScript)厨疙。HTML無法用解析器所需的上下文無關(guān)的語法來定義梗醇。過去HTML格式規(guī)范由DTD (Document Type Definition)來定義叙谨,但它不是一個(gè)上下文無關(guān)語法。

HTML與XML相當(dāng)接近。XML有許多可用的解析器统捶。HTML還有一個(gè)XML變種叫XHTML,那么它們主要區(qū)別在哪里呢什黑?區(qū)別在于HTML應(yīng)用更加”寬容”凯力,它容許你漏掉一些開始或結(jié)束標(biāo)簽等。它整個(gè)是一個(gè)“軟”句法祈惶,不像XML那樣嚴(yán)格死板。 總的來說這一看似細(xì)微的差別造成了兩個(gè)不同的世界。一方面這使得HTML很流行可款,因?yàn)樗菽愕腻e(cuò)誤,使網(wǎng)頁(yè)作者的生活變得輕松摸恍。另一方面,它使編寫語法格式變得困難。所以綜合來說,HTML解析并不簡(jiǎn)單恼琼,現(xiàn)成的上下文相關(guān)解析器搞不定,XML解析器也不行颤难。

HTML DTD

HTML的定義使用DTD文件。這種格式用來定義SGML族語言栅屏,它包含對(duì)所有允許的元素的定義,包括它們的屬性和層級(jí)關(guān)系。如我們前面所說蛀骇,HTML DTD構(gòu)不成上下文無關(guān)語法。

DTD有幾種不同類型岛马。嚴(yán)格模式完全尊守規(guī)范伞矩,但其它模式為了向前兼容可能包含對(duì)早期瀏覽器所用標(biāo)簽的支持。當(dāng)前的嚴(yán)格模式DTD:http://www.w3.org/TR/html4/strict.dtd

DOM

解析器輸出的樹是由DOM元素和屬性節(jié)點(diǎn)組成的湿诊。DOM的全稱為:Document Object Model。它是HTML文檔的對(duì)象化描述错沽,也是HTML元素與外界(如Javascript)的接口。

DOM與標(biāo)簽幾乎有著一一對(duì)應(yīng)的關(guān)系,如下面的標(biāo)簽

<html>

<body>

<p>Hello World.</p>

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

</body>


會(huì)被轉(zhuǎn)換成如的DOM樹:

Figure 8: DOM tree of the example markup

與HTML一樣,DOM規(guī)范也由w3c組織制訂。參考:http://www.w3.org/DOM/DOMTR. 這是一個(gè)操作文檔的通用規(guī)范织堂。有一個(gè)專門的模塊定義HTML特有元素:http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.html.

當(dāng)我們說樹中包含DOM節(jié)點(diǎn)時(shí),意思就是這個(gè)樹是由實(shí)現(xiàn)了DOM接口的元素組成。這些實(shí)現(xiàn)包含了其它一些瀏覽器內(nèi)部所需的屬性。

解析算法

如我們前面看到的肖爵,HTML無法使用自上而下或自下而上的解析器來解析。

理由如下:

語言的寬容特點(diǎn)

瀏覽器需要對(duì)無效HTML提供容錯(cuò)性的事實(shí)。

解析過程的反復(fù)。通常解析過程中源碼不會(huì)變化葛超。但在HTML中答渔,script標(biāo)簽包含”document.write”時(shí)可以添加內(nèi)容,即解析過程實(shí)際上還會(huì)改變?cè)创a。

瀏覽器創(chuàng)建了自己的解析器來解析HTML文檔笼沥。

HTML5規(guī)范里對(duì)解析算法有具體的說明诗良,解析由兩部分組成:分詞與構(gòu)建樹舞骆。

分詞屬于詞法分析部分,它把輸入解析成符號(hào)序列绪穆。在HTML中符號(hào)就是開始標(biāo)簽菠红,結(jié)束標(biāo)簽,屬性名稱和屬生值键袱。

分詞器識(shí)別這些符號(hào)并將其送入樹構(gòu)建者,然后繼續(xù)分析處理下一個(gè)符號(hào),直到輸入結(jié)束俊抵。

圖 6: HTML解析流程 (源自HTML5規(guī)范)

分詞算法

算法的輸出是HTML符號(hào)烛缔。算法可以用狀態(tài)機(jī)來描述。 每一個(gè)狀態(tài)從輸入流中消費(fèi)一個(gè)或多個(gè)字符喷舀,并根據(jù)它們更新下一狀態(tài)樊卓。決策受當(dāng)前符號(hào)狀態(tài)和樹的構(gòu)建狀態(tài)影響浇辜。這意味著同樣的字符可能會(huì)產(chǎn)生不同的結(jié)果,取決于當(dāng)前的狀態(tài)。算法太復(fù)雜绪囱,我們用一個(gè)例子來看看它的原理。

基礎(chǔ)示例,分析下面的標(biāo)簽:

Hello world

初始狀態(tài)是”Data state”粹排,當(dāng)遇到”<“時(shí)狀態(tài)改為“Tag open state”妙同。吃掉”a-z”字符組成的符號(hào)后產(chǎn)生了”Start tag token”,狀態(tài)變更為“Tag name state”粥帚。我們一直保持此狀態(tài)胰耗,直到遇到”>”。每個(gè)字符都被追加到新的符號(hào)名上芒涡。在我們的例子中,解出的符號(hào)就是”html”费尽。

當(dāng)碰到”>”時(shí)赠群,當(dāng)前符號(hào)完成,狀態(tài)改回“Data state”旱幼〔槊瑁””標(biāo)簽將會(huì)以同樣的方式處理。現(xiàn)在”html”與”body”標(biāo)簽都完成了柏卤,我們回到“Data state”狀態(tài)叹誉。吃掉”H”(”Hello world”第一個(gè)字母)時(shí)會(huì)產(chǎn)生一個(gè)字符符號(hào),直到碰到””的”<“符號(hào)闷旧,我們就完成了一個(gè)字符符號(hào)”Hello world”。

現(xiàn)在我們回到“Tag open state”狀態(tài)钧唐。吃掉下一個(gè)輸入”/”時(shí)會(huì)產(chǎn)生一個(gè)”end tag token”并變更為“Tag name state”狀態(tài)忙灼。同樣,此狀態(tài)保持到我們碰到”>”時(shí)。這時(shí)新標(biāo)簽符號(hào)完成该园,我們又回到“Data state”酸舍。同樣””也會(huì)被這樣處理。

圖 9: 示例輸入源的分詞處理

樹的構(gòu)建算法

當(dāng)解析器被創(chuàng)建時(shí)里初,文檔對(duì)象也被創(chuàng)建了啃勉。在樹的構(gòu)建過程中DOM樹的根節(jié)點(diǎn)(Documen)將被修改,元素被添加到上面去双妨。每個(gè)分詞器完成的節(jié)點(diǎn)都會(huì)被樹構(gòu)建器處理淮阐。規(guī)范中定義了每一個(gè)符號(hào)與哪個(gè)DOM對(duì)象相關(guān)。除了把元素添加到DOM樹外刁品,它還會(huì)被添加到一個(gè)開放元素堆棧泣特。這個(gè)堆棧用于糾正嵌套錯(cuò)誤和標(biāo)簽未關(guān)閉錯(cuò)誤。這個(gè)算法也用狀態(tài)機(jī)描述挑随,它的狀態(tài)叫做”insertion modes”状您。

讓我們看看下面輸入的樹構(gòu)建過程:

Hello world

樹的構(gòu)建過程中,輸入就是分詞過程中得到的符號(hào)序列兜挨。第一個(gè)模式叫“initial mode”膏孟。接收 html 符號(hào)后會(huì)變成“before html”模式并重新處理此模式中的符號(hào)。這會(huì)創(chuàng)建一個(gè)HTMLHtmlElement元素并追加到根文檔節(jié)點(diǎn)拌汇。

然后狀態(tài)改變?yōu)?b>“before head”柒桑。我們收到”body”時(shí),會(huì)隱式創(chuàng)建一個(gè)HTMLHeadElement担猛,盡管我們沒有這個(gè)標(biāo)簽幕垦,它也會(huì)被創(chuàng)建并添加到樹中。

現(xiàn)在我們進(jìn)入“in head”模式傅联,然后是“after head”先改,Body會(huì)被重新處理,創(chuàng)建HTMLBodyElement元素并插入蒸走,然后進(jìn)入“in body”模式仇奶。

字符符號(hào)”Hello world”收到后會(huì)創(chuàng)建一個(gè)”Text”節(jié)點(diǎn),所有字符都被一一追加到上面比驻。

收到body結(jié)束標(biāo)簽后進(jìn)入“after body”模式该溯,收到html結(jié)束標(biāo)簽后進(jìn)入“after after body”模式。所有符號(hào)處理完后將終止解析别惦。

圖 10: 示例HTML樹的構(gòu)建

解析結(jié)束后的動(dòng)作

在這一階段瀏覽器會(huì)把文檔標(biāo)記為交互模式狈茉,并開始解析deferred模式的script〉УВ”deferred”意味著腳本應(yīng)該在文檔解析完成后執(zhí)行氯庆。腳本處理完成后將進(jìn)入”complete”狀態(tài)蹭秋,”load”事件發(fā)生。

HTML5規(guī)范中包含了完整的算法:http://www.w3.org/TR/html5/syntax.html#html-parser

瀏覽器的容錯(cuò)

你永遠(yuǎn)不會(huì)看到HTML頁(yè)面語法錯(cuò)誤堤撵。瀏覽器會(huì)修正錯(cuò)誤并繼續(xù)仁讨。看看下面的例子:

Really lousy HTML

我一定違背了幾百萬條規(guī)則(”my tag”是非法標(biāo)簽实昨,”p”與”div”元素嵌套錯(cuò)誤等等)洞豁,但瀏覽器仍然正確地顯示,沒有任何抱怨荒给。所以很多解析器代碼在修正這些HTML作者的錯(cuò)誤丈挟。

瀏覽器的錯(cuò)誤處理相當(dāng)統(tǒng)一,驚人的是這并不是當(dāng)前HTML規(guī)范的一部分锐墙,就像書簽礁哄、前進(jìn)、后退溪北,只是多年以來在瀏覽器中開發(fā)出來的桐绒。有些無效的HTML結(jié)構(gòu)出現(xiàn)在許多網(wǎng)站,瀏覽器會(huì)嘗試用和其它各種瀏覽器一致的方式修復(fù)這些錯(cuò)誤之拨。

HTML5規(guī)范中應(yīng)這一需求定義了一些東西茉继,Webkit在它的HTML解析器類開頭的注釋中很好的做了摘要:

解析器分析輸入符號(hào)生成文檔,并構(gòu)建文檔樹蚀乔。如果文檔格式良好烁竭,解析工作會(huì)很簡(jiǎn)單。

不幸的是吉挣,我們要處理很多格式不良的HTML文檔派撕,解析器需要寬容這些錯(cuò)誤。

我們至少需要照顧下列錯(cuò)誤:

1. 元素必需被插入在正確的位置睬魂。未關(guān)閉的標(biāo)簽應(yīng)該一一關(guān)閉终吼,直到可以添加新元素。

2. 不允許直接添加元素氯哮。用戶可能會(huì)漏掉一些標(biāo)簽际跪,比如:HTML HEAD BODY TBODY TR TD LI(我遺漏了什么?)喉钢。

3. 在inline元素里添加block元素時(shí)姆打,應(yīng)關(guān)閉所有inline元素,再添加block元素肠虽。

4. 如果以上不起作用幔戏,關(guān)閉所有元素,直到可以添加税课,或者忽略此標(biāo)簽评抚。

讓我們來看一些Webkit容錯(cuò)的例子:

使用
代替

有些站點(diǎn)使用
而不是
豹缀。為了更好的與IE和Firefox兼容,Webkit將其視為
慨代。代碼如下:

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

reportError(MalformedBRError);

t->beginTag = true;

}

注意,這里的錯(cuò)誤處理是內(nèi)部的啸如,并不會(huì)顯示給用戶侍匙。

迷失的表格

像下面的例子這樣,一個(gè)表格包含在另外一個(gè)表格的內(nèi)容中叮雳,但不是在外部表格的單元格里:

inner table

outer table

Webkit會(huì)改變層級(jí)關(guān)系想暗,把它們處理成兩個(gè)相臨的表格:

outer table

inner table

代碼:

if (m_inStrayTableContent && localName == tableTag)

popBlock(tableTag);

Webkit用一個(gè)堆棧保存當(dāng)前元素,它會(huì)把里面的表格彈出到外部表格堆棧帘不,使它們成為兄弟表格说莫。

元素嵌套

為防止一表單的嵌套,第二個(gè)表單會(huì)被忽略寞焙。代碼:

if (!m_currentFormElement) {

m_currentFormElement = new HTMLFormElement(formTag,? ? m_document);

}

過深的元素層級(jí)

注釋不言自喻:

www.liceo.edu.mx是一個(gè)層級(jí)過深的典型储狭,它用大量的嵌套到1500個(gè)標(biāo)簽的深度。我們只允許同一標(biāo)簽連續(xù)出現(xiàn)20次捣郊,超過的話辽狈,所有此標(biāo)簽都會(huì)被忽略。

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;

}

錯(cuò)誤的html或body結(jié)束標(biāo)簽位置

注釋仍然很明了:

支持真正的錯(cuò)誤html

我們永遠(yuǎn)不關(guān)閉tag呛牲,因?yàn)橛行┯薮赖木W(wǎng)頁(yè)在文檔真正結(jié)束之前就關(guān)閉了它刮萌。

讓我們用end()來關(guān)閉標(biāo)簽。

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

return;

所以網(wǎng)頁(yè)作者們小心了娘扩,除非你想寫一個(gè)Webkit容錯(cuò)的示例代碼着茸,否則請(qǐng)按正確格式書寫HTML。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末琐旁,一起剝皮案震驚了整個(gè)濱河市涮阔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌旋膳,老刑警劉巖澎语,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異验懊,居然都是意外死亡擅羞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門义图,熙熙樓的掌柜王于貴愁眉苦臉地迎上來减俏,“玉大人,你說我怎么就攤上這事碱工⊥蕹校” “怎么了奏夫?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)历筝。 經(jīng)常有香客問我酗昼,道長(zhǎng),這世上最難降的妖魔是什么梳猪? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任麻削,我火速辦了婚禮,結(jié)果婚禮上春弥,老公的妹妹穿的比我還像新娘呛哟。我一直安慰自己,他們只是感情好匿沛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布扫责。 她就那樣靜靜地躺著,像睡著了一般逃呼。 火紅的嫁衣襯著肌膚如雪鳖孤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天蜘渣,我揣著相機(jī)與錄音淌铐,去河邊找鬼。 笑死蔫缸,一個(gè)胖子當(dāng)著我的面吹牛腿准,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拾碌,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼吐葱,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了校翔?” 一聲冷哼從身側(cè)響起弟跑,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎防症,沒想到半個(gè)月后孟辑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蔫敲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年饲嗽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奈嘿。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡貌虾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出裙犹,到底是詐尸還是另有隱情尽狠,我是刑警寧澤衔憨,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站袄膏,受9級(jí)特大地震影響践图,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜哩陕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一平项、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧悍及,春花似錦、人聲如沸接癌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)缺猛。三九已至缨叫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荔燎,已是汗流浹背耻姥。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留有咨,地道東北人琐簇。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像座享,于是被迫代替她去往敵國(guó)和親婉商。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容