真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

[置頂]   現(xiàn)代瀏覽器的工作原理

簡介

成都創(chuàng)新互聯(lián)服務(wù)項目包括霍山網(wǎng)站建設(shè)、霍山網(wǎng)站制作、霍山網(wǎng)頁制作以及霍山網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,霍山網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到霍山省份的部分城市,未來相信會繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!

瀏覽器可以被認(rèn)為是使用最廣泛的軟件,本文將介紹瀏覽器的工 作原理,我們將看到,從你在地址欄輸入google.com到你看到google主頁過程中都發(fā)生了什么。

將討論的瀏覽器

今天,有五種主流瀏覽器——IE、Firefox、Safari、Chrome及Opera。本文將基于一些開源瀏覽器的例子——Firefox、 Chrome及Safari,Safari是部分開源的。

根據(jù)W3C(World Wide Web Consortium 萬維網(wǎng)聯(lián)盟)的瀏覽器統(tǒng)計數(shù)據(jù),當(dāng)前(2011年9月),F(xiàn)irefox、Safari及Chrome的市場占有率綜合已快接近50%。(原文為2009年10月,數(shù)據(jù)沒有太大變化)因此,可以說開源瀏覽器將近占據(jù)了瀏覽器市場的半壁江山。

瀏覽器的主要功能

瀏覽器的主要功能是將用戶選擇得web資源呈現(xiàn)出來,它需要從服務(wù)器請求資源,并將其顯示在瀏覽器窗口中,資源的格式通常是HTML,也包括PDF、image及其他格式。用戶用URI(Uniform Resource Identifier 統(tǒng)一資源標(biāo)識符)來指定所請求資源的位置,在網(wǎng)絡(luò)一章有更多討論。

HTML和CSS規(guī)范中規(guī)定了瀏覽器解釋html文檔的方式,由 W3C組織對這些規(guī)范進(jìn)行維護(hù),W3C是負(fù)責(zé)制定web標(biāo)準(zhǔn)的組織。

HTML規(guī)范的最新版本是HTML4(http://www.w3.org/TR/html401/),HTML5還在制定中(譯注:兩年前),最新的CSS規(guī)范版本是2(http://www.w3.org/TR/CSS2),CSS3也還正在制定中(譯注:同樣兩年前)。

這些年來,瀏覽器廠商紛紛開發(fā)自己的擴(kuò)展,對規(guī)范的遵循并不完善,這為web開發(fā)者帶來了嚴(yán)重的兼容性問題。

但是,瀏覽器的用戶界面則差不多,常見的用戶界面元素包括:

▲用來輸入URI的地址欄

▲前進(jìn)、后退按鈕

▲書簽選項

▲用于刷新及暫停當(dāng)前加載文檔的刷新、暫停按鈕

▲用于到達(dá)主頁的主頁按鈕

奇怪的是,并沒有哪個正式公布的規(guī)范對用戶界面做出規(guī)定,這些是多年來各瀏覽器廠商之間相互模仿和不斷改進(jìn)得結(jié)果。

HTML5并沒有規(guī)定瀏覽器必須具有的UI元素,但列出了一些常用元素,包括地址欄、狀態(tài)欄及工具欄。還有一些瀏覽器有自己專有得功能,比如Firefox得下載管理。更多相關(guān)內(nèi)容將在后面討論用戶界面時介紹。

瀏覽器的主要構(gòu)成High Level Structure

瀏覽器的主要組件包括:

▲用戶界面- 包括地址欄、后退/前進(jìn)按鈕、書簽?zāi)夸浀?,也就是你所看到的除了用來顯示你所請求頁面的主窗口之外的其他部分

▲瀏覽器引擎- 用來查詢及操作渲染引擎的接口

▲渲染引擎- 用來顯示請求的內(nèi)容,例如,如果請求內(nèi)容為html,它負(fù)責(zé)解析html及css,并將解析后的結(jié)果顯示出來

▲網(wǎng)絡(luò)- 用來完成網(wǎng)絡(luò)調(diào)用,例如http請求,它具有平臺無關(guān)的接口,可以在不同平臺上工作
UI 后端- 用來繪制類似組合選擇框及對話框等基本組件,具有不特定于某個平臺的通用接口,底層使用操作系統(tǒng)的用戶接口

▲JS解釋器- 用來解釋執(zhí)行JS代碼

▲數(shù)據(jù)存儲- 屬于持久層,瀏覽器需要在硬盤中保存類似cookie的各種數(shù)據(jù),HTML5定義了web database技術(shù),這是一種輕量級完整的客戶端存儲技術(shù)

[置頂]          現(xiàn)代瀏覽器的工作原理

圖1:瀏覽器主要組件

需要注意的是,不同于大部分瀏覽器,Chrome為每個Tab分配了各自的渲染引擎實例,每個Tab就是一個獨立的進(jìn)程。

對于構(gòu)成瀏覽器的這些組件,后面會逐一詳細(xì)討論。

組件間的通信 Communication between the components

Firefox和Chrome都開發(fā)了一個特殊的通信結(jié)構(gòu),后面將有專門的一章進(jìn)行討論。

渲染引擎 The rendering engine

渲染引擎的職責(zé)就是渲染,即在瀏覽器窗口中顯示所請求的內(nèi)容。

默認(rèn)情況下,渲染引擎可以顯示html、xml文檔及圖片,它也可以借助插件(一種瀏覽器擴(kuò)展)顯示其他類型數(shù)據(jù),例如使用PDF閱讀器插件,可以顯示PDF格式,將由專門一章講解插件及擴(kuò)展,這里只討論渲染引擎最主要的用途——顯示應(yīng)用了CSS之后的html及圖片。

渲染引擎 Rendering engines

本文所討論得瀏覽器——Firefox、Chrome和Safari是基于兩種渲染引擎構(gòu)建的,F(xiàn)irefox使用Geoko——Mozilla自主研發(fā)的渲染引擎,Safari和Chrome都使用webkit。

Webkit是一款開源渲染引擎,它本來是為linux平臺研發(fā)的,后來由Apple移植到Mac及Windows上,相關(guān)內(nèi)容請參考http://webkit.org。

主流程 The main flow

渲染引擎首先通過網(wǎng)絡(luò)獲得所請求文檔的內(nèi)容,通常以8K分塊的方式完成。

下面是渲染引擎在取得內(nèi)容之后的基本流程:

解析html以構(gòu)建dom樹->構(gòu)建render樹->布局render樹->繪制render樹

[置頂]          現(xiàn)代瀏覽器的工作原理

圖2:渲染引擎基本流程

渲染引擎開始解析html,并將標(biāo)簽轉(zhuǎn)化為內(nèi)容樹中的dom節(jié)點。接著,它解析外部CSS文件及style標(biāo)簽中的樣式信息。這些樣式信息以及html中的可見性指令將被用來構(gòu)建另一棵樹——render樹。

Render樹由一些包含有顏色和大小等屬性的矩形組成,它們將被按照正確的順序顯示到屏幕上。

Render樹構(gòu)建好了之后,將會執(zhí)行布局過程,它將確定每個節(jié)點在屏幕上的確切坐標(biāo)。再下一步就是繪制,即遍歷render樹,并使用UI后端層繪制每個節(jié)點。

值得注意的是,這個過程是逐步完成的,為了更好的用戶體驗,渲染引擎將會盡可能早的將內(nèi)容呈現(xiàn)到屏幕上,并不會等到所有的html都解析完成之后再去構(gòu)建和布局render樹。它是解析完一部分內(nèi)容就顯示一部分內(nèi)容,同時,可能還在通過網(wǎng)絡(luò)下載其余內(nèi)容。

 [置頂]          現(xiàn)代瀏覽器的工作原理

圖3:webkit主流程

 [置頂]          現(xiàn)代瀏覽器的工作原理

圖4:Mozilla的Geoko 渲染引擎主流程

從圖3和4中可以看出,盡管webkit和Gecko使用的術(shù)語稍有不同,他們的主要流程基本相同。Gecko稱可見的格式化元素組成的樹為frame樹,每個元素都是一個frame,webkit則使用render樹這個名詞來命名由渲染對象組成的樹。Webkit中元素的定位稱為布局,而Gecko中稱為回流。Webkit稱利用dom節(jié)點及樣式信息去構(gòu)建render樹的過程為attachment,Gecko在html和dom樹之間附加了一層,這層稱為內(nèi)容接收器,相當(dāng)制造dom元素的工廠。下面將討論流程中的各個階段。

解析 Parsing-general

既然解析是渲染引擎中一個非常重要的過程,我們將稍微深入的研究它。首先簡要介紹一下解析。

解析一個文檔即將其轉(zhuǎn)換為具有一定意義的結(jié)構(gòu)——編碼可以理解和使用的東西。解析的結(jié)果通常是表達(dá)文檔結(jié)構(gòu)的節(jié)點樹,稱為解析樹或語法樹。

例如,解析“2+3-1”這個表達(dá)式,可能返回這樣一棵樹。

[置頂]          現(xiàn)代瀏覽器的工作原理

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

文法 Grammars

解析基于文檔依據(jù)的語法規(guī)則——文檔的語言或格式。每種可被解析的格式必須具有由詞匯及語法規(guī)則組成的特定的文法,稱為上下文無關(guān)文法。人類語言不具有這一特性,因此不能被一般的解析技術(shù)所解析。

解析器-詞法分析器 Parser-Lexer combination

解析可以分為兩個子過程——語法分析及詞法分析

詞法分析就是將輸入分解為符號,符號是語言的詞匯表——基本有效單元的集合。對于人類語言來說,它相當(dāng)于我們字典中出現(xiàn)的所有單詞。

語法分析指對語言應(yīng)用語法規(guī)則。

解析器一般將工作分配給兩個組件——詞法分析器(有時也叫分詞器)負(fù)責(zé)將輸入分解為合法的符號,解析器則根據(jù)語言的語法規(guī)則分析文檔結(jié)構(gòu),從而構(gòu)建解析樹,詞法分析器知道怎么跳過空白和換行之類的無關(guān)字符。

[置頂]          現(xiàn)代瀏覽器的工作原理

圖6:從源文檔到解析樹

解析過程是迭代的,解析器從詞法分析器處取道一個新的符號,并試著用這個符號匹配一條語法規(guī)則, 如果匹配了一條規(guī)則,這個符號對應(yīng)的節(jié)點將被添加到解析樹上,然后解析器請求另一個符號。如果沒有匹配到規(guī)則,解析器將在內(nèi)部保存該符號,并從詞法分析器 取下一個符號,直到所有內(nèi)部保存的符號能夠匹配一項語法規(guī)則。如果最終沒有找到匹配的規(guī)則,解析器將拋出一個異常,這意味著文檔無效或是包含語法錯誤。

轉(zhuǎn)換 Translation

很多時候,解析樹并不是最終結(jié)果。解析一般在轉(zhuǎn)換中使用——將輸入文檔轉(zhuǎn)換為另一種格式。編譯就是個例子,編譯器在將一段源碼編譯為機(jī)器碼的時候,先將源碼解析為解析樹,然后將該樹轉(zhuǎn)換為一個機(jī)器碼文檔。

[置頂]          現(xiàn)代瀏覽器的工作原理

圖7:編譯流程

解析實例 Parsing example

圖5中,我們從一個數(shù)學(xué)表達(dá)式構(gòu)建了一個解析樹,這里定義一個簡單的數(shù)學(xué)語言來看下解析過程。

詞匯表:我們的語言包括整數(shù)、加號及減號。

語法:

1. 該語言的語法基本單元包括表達(dá)式、term及操作符

2. 該語言可以包括多個表達(dá)式

3. 一個表達(dá)式定義為兩個term通過一個操作符連接

4. 操作符可以是加號或減號

5. term可以是一個整數(shù)或一個表達(dá)式

現(xiàn)在來分析一下“2+3-1”這個輸入

第一個匹配規(guī)則的子字符串是“2”,根據(jù)規(guī)則5,它是一個term,第二個匹配的是“2+3”,它符合第2條規(guī)則——一個操作符連接兩個term,下一次匹配發(fā)生在輸入的結(jié)束處。“2+3-1”是一個表達(dá)式,因為我們已經(jīng)知道“2+3”是一個term,所以我們有了一個term緊跟著一個操作符及另一個term?!?++”將不會匹配任何規(guī)則,因此是一個無效輸入。

詞匯表及語法的定義

詞匯表通常利用正則表達(dá)式來定義。

例如上面的語言可以定義為:

 

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

 

正如看到的,這里用正則表達(dá)式定義整數(shù)。

語法通常用BNF格式定義,我們的語言可以定義為:

 

1
2
3
expression := term operation term
operation := PLUS | MINUS
term := INTEGER | expression

 

如果一個語言的文法是上下文無關(guān)的,則它可以用正則解析器來解析。對上下文無關(guān)文法的一個直觀的定義是,該文法可以用BNF來完整的表達(dá)??刹榭磆ttp://en.wikipedia.org/wiki/Context-free_grammar。

解析器類型 Types of parsers

有兩種基本的解析器——自頂向下解析及自底向上解析。比較直觀的解釋是,自頂向下解析,查看語法的最高層結(jié)構(gòu)并試著匹配其中一個;自底向上解析則從輸入開始,逐步將其轉(zhuǎn)換為語法規(guī)則,從底層規(guī)則開始直到匹配高層規(guī)則。

來看一下這兩種解析器如何解析上面的例子:

自頂向下解析器從最高層規(guī)則開始——它先識別出“2+3“,將其視為一個表達(dá)式,然后識別出”2+3-1“為一個表達(dá)式(識別表達(dá)式的過程中匹配了其他規(guī)則,但出發(fā)點是最高層規(guī)則)。

自底向上解析會掃描輸入直到匹配了一條規(guī)則,然后用該規(guī)則取代匹配的輸入,直到解析完所有輸入。部分匹配的表達(dá)式被放置在解析堆棧中。

 

Stack

Input

  2 + 3 – 1
term + 3 – 1
term operation 3 – 1
expression - 1
expression operation 1
expression  

自底向上解析器稱為shift reduce 解析器,因為輸入向右移動(想象一個指針首先指向輸入開始處,并向右移動),并逐漸簡化為語法規(guī)則。

自動化解析 Generating parse

解析器生成器這個工具可以自動生成解析器,只需要指定語言的文法——詞匯表及語法規(guī)則,它就可以生成一個解析器。創(chuàng)建一個解析器需要對解析有深入的理解,而且手動的創(chuàng)建一個由較好性能的解析器并不容易,所以解析生成器很有用。Webkit使用兩個知名的解析生成器——用于創(chuàng)建語法分析器的Flex及創(chuàng)建解析器的Bison(你可能接觸過Lex和Yacc)。Flex的輸入是一個包含了符號定義的正則表達(dá)式,Bison的輸入是用BNF格式表示的語法規(guī)則。rs automatically

HTML解析器 HTML Parser

HTML解析器的工作是將html標(biāo)識解析為解析樹。

HTML文法定義 The HTML grammar definition

W3C組織制定規(guī)范定義了HTML的詞匯表和語法。

非上下文無關(guān)文法 Not a context free grammar

正如在解析簡介中提到的,上下文無關(guān)文法的語法可以用類似BNF的格式來定義。

不幸的是,所有的傳統(tǒng)解析方式都不適用于html(當(dāng)然我提出它們并不只是因為好玩,它們將用來解析css和js),html不能簡單的用解析所需的上下文無關(guān)文法來定義。

Html 有一個正式的格式定義——DTD(Document Type Definition 文檔類型定義)——但它并不是上下文無關(guān)文法,html更接近于xml,現(xiàn)在有很多可用的xml解析器,html有個xml的變體——xhtml,它們間的不同在于,html更寬容,它允許忽略一些特定標(biāo)簽,有時可以省略開始或結(jié)束標(biāo)簽??偟膩碚f,它是一種soft語法,不像xml呆板、固執(zhí)。

顯然,這個看起來很小的差異卻帶來了很大的不同。一方面,這是html流行的原因——它的寬容使web開發(fā)人員的工作更加輕松,但另一方面,這也使很難去寫一個格式化的文法。所以,html的解析并不簡單,它既不能用傳統(tǒng)的解析器解析,也不能用xml解析器解析。

HTML DTD

Html適用DTD格式進(jìn)行定義,這一格式是用于定義SGML家族的語言,包括了對所有允許元素及它們的屬性和層次關(guān)系的定義。正如前面提到的,html DTD并沒有生成一種上下文無關(guān)文法。

DTD有一些變種,標(biāo)準(zhǔn)模式只遵守規(guī)范,而其他模式則包含了對瀏覽器過去所使用標(biāo)簽的支持,這么做是為了兼容以前內(nèi)容。最新的標(biāo)準(zhǔn)DTD在http://www.w3.org/TR/html4/strict.dtd

DOM

輸出的樹,也就是解析樹,是由DOM元素及屬性節(jié)點組成的。DOM是文檔對象模型的縮寫,它是html文檔的對象表示,作為html元素的外部接口供js等調(diào)用。

樹的根是“document”對象。

DOM和標(biāo)簽基本是一一對應(yīng)的關(guān)系,例如,如下的標(biāo)簽:

 

1
2
3
4
5
6
7
8
<html>
<body>
<p>
Hello DOM
p>
<div><img src=”example.png” />div>
body>
html>

 

將會被轉(zhuǎn)換為下面的DOM樹:

[置頂]          現(xiàn)代瀏覽器的工作原理

圖8:示例標(biāo)簽對應(yīng)的DOM樹

和html一樣,DOM的規(guī)范也是由W3C組織制定的。訪問http://www.w3.org/DOM/DOMTR,這是使用文檔的一般規(guī)范。一個模型描述一種特定的html元素,可以在http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.htm 查看html定義。

這里所謂的樹包含了DOM節(jié)點是說樹是由實現(xiàn)了DOM接口的元素構(gòu)建而成的,瀏覽器使用已被瀏覽器內(nèi)部使用的其他屬性的具體實現(xiàn)。

解析算法 The parsing algorithm

正如前面章節(jié)中討論的,hmtl不能被一般的自頂向下或自底向上的解析器所解析。

原因是:

1. 這門語言本身的寬容特性

2. 瀏覽器對一些常見的非法html有容錯機(jī)制

3. 解析過程是往復(fù)的,通常源碼不會在解析過程中發(fā)生改變,但在html中,腳本標(biāo)簽包含的“document.write ”可能添加標(biāo)簽,這說明在解析過程中實際上修改了輸入

不能使用正則解析技術(shù),瀏覽器為html定制了專屬的解析器。

Html5規(guī)范中描述了這個解析算法,算法包括兩個階段——符號化及構(gòu)建樹。

符號化是詞法分析的過程,將輸入解析為符號,html的符號包括開始標(biāo)簽、結(jié)束標(biāo)簽、屬性名及屬性值。

符號識別器識別出符號后,將其傳遞給樹構(gòu)建器,并讀取下一個字符,以識別下一個符號,這樣直到處理完所有輸入。

[置頂]          現(xiàn)代瀏覽器的工作原理

圖9:HTML解析流程

符號識別算法 The tokenization algorithm

算法輸出html符號,該算法用狀態(tài)機(jī)表示。每次讀取輸入流中的一個或多個字符,并根據(jù)這些字符轉(zhuǎn)移到下一個狀態(tài),當(dāng)前的符號狀態(tài)及構(gòu)建樹狀態(tài)共同影響結(jié)果,這意味著,讀取同樣的字符,可能因為當(dāng)前狀態(tài)的不同,得到不同的結(jié)果以進(jìn)入下一個正確的狀態(tài)。

這個算法很復(fù)雜,這里用一個簡單的例子來解釋這個原理。

基本示例——符號化下面的html:

 

1
2
3
4
5
<html>
<body>
Hello world
body>
html>

 

初始狀態(tài)為“Data State”,當(dāng)遇到“<”字符,狀態(tài)變?yōu)椤癟ag open state”,讀取一個a-z的字符將產(chǎn)生一個開始標(biāo)簽符號,狀態(tài)相應(yīng)變?yōu)椤癟ag name state”,一直保持這個狀態(tài)直到讀取到“>”,每個字符都附加到這個符號名上,例子中創(chuàng)建的是一個html符號。

當(dāng)讀取到“>”,當(dāng)前的符號就完成了,此時,狀態(tài)回到“Data state”,“”重復(fù)這一處理過程。到這里,html和body標(biāo)簽都識別出來了?,F(xiàn)在,回到“Data state”,讀取“Hello world”中的字符“H”將創(chuàng)建并識別出一個字符符號,這里會為“Hello world”中的每個字符生成一個字符符號。

這樣直到遇到“”中的“<”。現(xiàn)在,又回到了“Tag open state”,讀取下一個字符“/”將創(chuàng)建一個閉合標(biāo)簽符號,并且狀態(tài)轉(zhuǎn)移到“Tag name state”,還是保持這一狀態(tài),直到遇到“>”。然后,產(chǎn)生一個新的標(biāo)簽符號并回到“Data state”。后面的“”將和“”一樣處理。

[置頂]          現(xiàn)代瀏覽器的工作原理

圖10:符號化示例輸入

樹的構(gòu)建算法 Tree construction algorithm

在樹的構(gòu)建階段,將修改以Document為根的DOM樹,將元素附加到樹上。每個由符號識別器識別生成的節(jié)點將會被樹構(gòu)造器進(jìn)行處理,規(guī)范中定義了每個符號相對應(yīng)的Dom元素,對應(yīng)的Dom元素將會被創(chuàng)建。這些元素除了會被添加到Dom樹上,還將被添加到開放元素堆棧中。這個堆棧用來糾正嵌套的未匹配和未閉合標(biāo)簽,這個算法也是用狀態(tài)機(jī)來描述,所有的狀態(tài)采用插入模式。

來看一下示例中樹的創(chuàng)建過程:

 

1
2
3
4
5
<html>
<body>
Hello world
body>
html>

 

構(gòu)建樹這一階段的輸入是符號識別階段生成的符號序列。

首先是“initial mode”,接收到html符號后將轉(zhuǎn)換為“before html”模式,在這個模式中對這個符號進(jìn)行再處理。此時,創(chuàng)建了一個HTMLHtmlElement元素,并將其附加到根Document對象上。

狀態(tài)此時變?yōu)椤癰efore head”,接收到body符號時,即使這里沒有head符號,也將自動創(chuàng)建一個HTMLHeadElement元素并附加到樹上。

現(xiàn)在,轉(zhuǎn)到“in head”模式,然后是“after head”。到這里,body符號會被再次處理,將創(chuàng)建一個HTMLBodyElement并插入到樹中,同時,轉(zhuǎn)移到“in body”模式。

然后,接收到字符串“Hello world”的字符符號,第一個字符將導(dǎo)致創(chuàng)建并插入一個text節(jié)點,其他字符將附加到該節(jié)點。

接收到body結(jié)束符號時,轉(zhuǎn)移到“after body”模式,接著接收到html結(jié)束符號,這個符號意味著轉(zhuǎn)移到了“after after body”模式,當(dāng)接收到文件結(jié)束符時,整個解析過程結(jié)束。

[置頂]          現(xiàn)代瀏覽器的工作原理

圖11:示例html樹的構(gòu)建過程

解析結(jié)束時的處理 Action when the parsing is finished

在這個階段,瀏覽器將文檔標(biāo)記為可交互的,并開始解析處于延時模式中的腳本——這些腳本在文檔解析后執(zhí)行。

文檔狀態(tài)將被設(shè)置為完成,同時觸發(fā)一個load事件。

Html5規(guī)范中有符號化及構(gòu)建樹的完整算法(http://www.w3.org/TR/html5/syntax.html#html-parser)。

瀏覽器容錯 Browsers error tolerance

你從來不會在一個html頁面上看到“無效語法”這樣的錯誤,瀏覽器修復(fù)了無效內(nèi)容并繼續(xù)工作。

以下面這段html為例:

 

1
2
3
4
5
6
7
8
9
<html>
<mytag>
mytag>
<div>
<p>
div>
Really lousy HTML
p>
html>

 

這段html違反了很多規(guī)則(mytag不是合法的標(biāo)簽,p及div錯誤的嵌套等等),但是瀏覽器仍然可以沒有任何怨言的繼續(xù)顯示,它在解析的過程中修復(fù)了html作者的錯誤。

瀏覽器都具有錯誤處理的能力,但是,另人驚訝的是,這并不是html最新規(guī)范的內(nèi)容,就像書簽及前進(jìn)后退按鈕一樣,它只是瀏覽器長期發(fā)展的結(jié)果。一些比較知名的非法html結(jié)構(gòu),在許多站點中出現(xiàn)過,瀏覽器都試著以一種和其他瀏覽器一致的方式去修復(fù)。

Html5規(guī)范定義了這方面的需求,webkit在html解析類開始部分的注釋中做了很好的總結(jié)。

解析器將符號化的輸入解析為文檔并創(chuàng)建文檔,但不幸的是,我們必須處理很多沒有很好格式化的html文檔,至少要小心下面幾種錯誤情況。

1. 在未閉合的標(biāo)簽中添加明確禁止的元素。這種情況下,應(yīng)該先將前一標(biāo)簽閉合

2. 不能直接添加元素。有些人在寫文檔的時候會忘了中間一些標(biāo)簽(或者中間標(biāo)簽是可選的),比如HTML HEAD BODY TR TD LI等

3. 想在一個行內(nèi)元素中添加塊狀元素。關(guān)閉所有的行內(nèi)元素,直到下一個更高的塊狀元素

4. 如果這些都不行,就閉合當(dāng)前標(biāo)簽直到可以添加該元素。

下面來看一些webkit容錯的例子:

1
br>替代

一些網(wǎng)站使用
替代
,為了兼容IE和Firefox,webkit將其看作
。

代碼:

 

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

 

Note-這里的錯誤處理在內(nèi)部進(jìn)行,用戶看不到。

迷路的表格

這指一個表格嵌套在另一個表格中,但不在它的某個單元格內(nèi)。

比如下面這個例子:

 

1
2
3
4
5
6
<table>
<table>
<tr><td>inner tabletd>tr>
table>
<tr><td>outer tabletd>tr>
table>

 

webkit將會將嵌套的表格變?yōu)閮蓚€兄弟表格:

 

1
2
3
4
5
6
<table>
<tr><td>outer tabletd>tr>
table>
<table>
<tr><td>inner tabletd>tr>
table>

 

代碼:

 

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

 

webkit使用堆棧存放當(dāng)前的元素內(nèi)容,它將從外部表格的堆棧中彈出內(nèi)部的表格,則它們變?yōu)榱诵值鼙?#26684;。

嵌套的表單元素

用戶將一個表單嵌套到另一個表單中,則第二個表單將被忽略。

代碼:

 

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

 

太深的標(biāo)簽繼承

www.liceo.edu.mx是一個由嵌套層次的站點的例子,最多只允許20個相同類型的標(biāo)簽嵌套,多出來的將被忽略。

代碼:

 

1
2
3
4
5
6
7
8
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;
}

 

放錯了地方的html、body閉合標(biāo)簽

又一次不言自明。

支持不完整的html。我們從來不閉合body,因為一些愚蠢的網(wǎng)頁總是在還未真正結(jié)束時就閉合它。我們依賴調(diào)用end方法去執(zhí)行關(guān)閉的處理。

代碼:

 

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

 

所以,web開發(fā)者要小心了,除非你想成為webkit容錯代碼的范例,否則還是寫格式良好的html吧。

CSS解析 CSS parsing

還記得簡介中提到的解析的概念嗎,不同于html,css屬于上下文無關(guān)文法,可以用前面所描述的解析器來解析。Css規(guī)范定義了css的詞法及語法文法。

看一些例子:

每個符號都由正則表達(dá)式定義了詞法文法(詞匯表):

 

1
2
3
4
5
6
7
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”是識別器的縮寫,相當(dāng)于一個class名,“name”是一個元素id(用“#”引用)。

語法用BNF進(jìn)行描述:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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*] ‘)’ ]
;

 

說明:一個規(guī)則集合有這樣的結(jié)構(gòu)

 

1
2
3
4
div.error , a.error {
color:red;
font-weight:bold;
}

 

div.error和a.error時選擇器,大括號中的內(nèi)容包含了這條規(guī)則集合中的規(guī)則,這個結(jié)構(gòu)在下面的定義中正式的定義了:

 

1
2
3
4
ruleset
: selector [ ',' S* selector ]*
‘{’ S* declaration [ ';' S* declaration ]* ‘}’ S*
;

 

這說明,一個規(guī)則集合具有一個或是可選個數(shù)的多個選擇器,這些選擇器以逗號和空格(S表示空格)進(jìn)行分隔。每個規(guī)則集合包含大括號及大括號中的一條或多條以分號隔開的聲明。聲明和選擇器在后面進(jìn)行定義。

Webkit CSS 解析器 Webkit CSS parser

Webkit使用Flex和Bison解析生成器從CSS語法文件中自動生成解析器?;貞浺幌陆馕銎鞯慕榻B,Bison創(chuàng)建一個自底向上的解析器,F(xiàn)irefox使用自頂向下解析器。它們都是將每個css文件解析為樣式表對象,每個對象包含css規(guī)則,css規(guī)則對象包含選擇器和聲明對象,以及其他一些符合css語法的對象。

[置頂]          現(xiàn)代瀏覽器的工作原理

圖12:解析css

腳本解析 Parsing scripts

本章將介紹Javascript。

處理腳本及樣式表的順序 The order of processing scripts and style sheets

腳本

web的模式是同步的,開發(fā)者希望解析到一個script標(biāo)簽時立即解析執(zhí)行腳本,并阻塞文檔的解析直到腳本執(zhí)行完。如果腳本是外引的,則網(wǎng)絡(luò)必須先請求到這個資源——這個過程也是同步的,會阻塞文檔的解析直到資源被請求到。這個模式保持了很多年,并且在html4及html5中都特別指定了。開發(fā)者可以將腳本標(biāo)識為defer,以使其不阻塞文檔解析,并在文檔解析結(jié)束后執(zhí)行。Html5增加了標(biāo)記腳本為異步的選項,以使腳本的解析執(zhí)行使用另一個線程。

預(yù)解析 Speculative parsing

Webkit和Firefox都做了這個優(yōu)化,當(dāng)執(zhí)行腳本時,另一個線程解析剩下的文檔,并加載后面需要通過網(wǎng)絡(luò)加載的資源。這種方式可以使資源并行加載從而使整體速度更快。需要注意的是,預(yù)解析并不改變Dom樹,它將這個工作留給主解析過程,自己只解析外部資源的引用,比如外部腳本、樣式表及圖片。

樣式表 Style sheets

樣式表采用另一種不同的模式。理論上,既然樣式表不改變Dom樹,也就沒有必要停下文檔的解析等待它們,然而,存在一個問題,腳本可能在文檔的解析過程中請求樣式信息,如果樣式還沒有加載和解析,腳本將得到錯誤的值,顯然這將會導(dǎo)致很多問題,這看起來是個邊緣情況,但確實很常見。Firefox在存在樣式表還在加載和解析時阻塞所有的腳本,而chrome只在當(dāng)腳本試圖訪問某些可能被未加載的樣式表所影響的特定的樣式屬性時才阻塞這些腳本。

渲染樹的構(gòu)造 Render tree construction

當(dāng)Dom樹構(gòu)建完成時,瀏覽器開始構(gòu)建另一棵樹——渲染樹。渲染樹由元素顯示序列中的可見元素組成,它是文檔的可視化表示,構(gòu)建這棵樹是為了以正確的順序繪制文檔內(nèi)容。

Firefox將渲染樹中的元素稱為frames,webkit則用renderer或渲染對象來描述這些元素。

一個渲染對象直到怎么布局及繪制自己及它的children。

RenderObject是Webkit的渲染對象基類,它的定義如下:

 

1
2
3
4
5
6
7
8
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
}

 

每個渲染對象用一個和該節(jié)點的css盒模型相對應(yīng)的矩形區(qū)域來表示,正如css2所描述的那樣,它包含諸如寬、高和位置之類的幾何信息。盒模型的類型受該節(jié)點相關(guān)的display樣式屬性的影響(參考樣式計算章節(jié))。下面的webkit代碼說明了如何根據(jù)display屬性決定某個節(jié)點創(chuàng)建何種類型的渲染對象。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;
} 文章標(biāo)題:[置頂]   現(xiàn)代瀏覽器的工作原理
URL分享:http://weahome.cn/article/jspocc.html

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部

    • <ul id="qkgyq"><sup id="qkgyq"></sup></ul>
    • <abbr id="qkgyq"></abbr>
      <tfoot id="qkgyq"></tfoot>
      <sup id="qkgyq"><samp id="qkgyq"></samp></sup>
      • <ul id="qkgyq"><sup id="qkgyq"></sup></ul>