日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > HTML >内容正文

HTML

现代浏览器的工作原理

發(fā)布時(shí)間:2024/4/13 HTML 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 现代浏览器的工作原理 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

英文原文:Tali Garsiel,編譯:zzzaquarius

簡(jiǎn)介

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

將討論的瀏覽器

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

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

瀏覽器的主要功能

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

HTML和CSS規(guī)范中規(guī)定了瀏覽器解釋html文檔的方式,由 W3C組織對(duì)這些規(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ò)展,對(duì)規(guī)范的遵循并不完善,這為web開發(fā)者帶來了嚴(yán)重的兼容性問題。

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

  • ▲用來輸入U(xiǎn)RI的地址欄
  • ▲前進(jìn)、后退按鈕
  • ▲書簽選項(xiàng)
  • ▲用于刷新及暫停當(dāng)前加載文檔的刷新、暫停按鈕
  • ▲用于到達(dá)主頁(yè)的主頁(yè)按鈕

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

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

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

瀏覽器的主要組件包括:

  • 用戶界面- 包括地址欄、后退/前進(jìn)按鈕、書簽?zāi)夸浀?#xff0c;也就是你所看到的除了用來顯示你所請(qǐng)求頁(yè)面的主窗口之外的其他部分
  • 瀏覽器引擎- 用來查詢及操作渲染引擎的接口
  • 渲染引擎- 用來顯示請(qǐng)求的內(nèi)容,例如,如果請(qǐng)求內(nèi)容為html,它負(fù)責(zé)解析html及css,并將解析后的結(jié)果顯示出來
  • 網(wǎng)絡(luò)- 用來完成網(wǎng)絡(luò)調(diào)用,例如http請(qǐng)求,它具有平臺(tái)無(wú)關(guān)的接口,可以在不同平臺(tái)上工作
  • UI 后端- 用來繪制類似組合選擇框及對(duì)話框等基本組件,具有不特定于某個(gè)平臺(tái)的通用接口,底層使用操作系統(tǒng)的用戶接口
  • JS解釋器- 用來解釋執(zhí)行JS代碼
  • 數(shù)據(jù)存儲(chǔ)- 屬于持久層,瀏覽器需要在硬盤中保存類似cookie的各種數(shù)據(jù),HTML5定義了web database技術(shù),這是一種輕量級(jí)完整的客戶端存儲(chǔ)技術(shù)

圖1:瀏覽器主要組件

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

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

組件間的通信 Communication between the components

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

渲染引擎 The rendering engine

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

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

渲染引擎 Rendering engines

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

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

主流程 The main flow

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

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

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

圖2:渲染引擎基本流程

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

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

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

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

圖3:webkit主流程

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

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

解析 Parsing-general

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

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

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

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

文法 Grammars

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

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

解析可以分為兩個(gè)子過程——語(yǔ)法分析及詞法分析

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

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

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

圖6:從源文檔到解析樹

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

轉(zhuǎn)換 Translation

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

圖7:編譯流程

解析實(shí)例 Parsing example

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

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

語(yǔ)法:

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

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

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

4. 操作符可以是加號(hào)或減號(hào)

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

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

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

詞匯表及語(yǔ)法的定義

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

例如上面的語(yǔ)言可以定義為:

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

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

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

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

如果一個(gè)語(yǔ)言的文法是上下文無(wú)關(guān)的,則它可以用正則解析器來解析。對(duì)上下文無(wú)關(guān)文法的一個(gè)直觀的定義是,該文法可以用BNF來完整的表達(dá)。可查看en.wikipedia.org/wiki/Contex…。

解析器類型 Types of parsers

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

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

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

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

Stack

Input

2 + 3 – 1
term+ 3 – 1
term operation3 – 1
expression– 1
expression operation1
expression

自底向上解析器稱為shift reduce 解析器,因?yàn)檩斎胂蛴乙苿?dòng)(想象一個(gè)指針首先指向輸入開始處,并向右移動(dòng)),并逐漸簡(jiǎn)化為語(yǔ)法規(guī)則。

自動(dòng)化解析 Generating parse

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

HTML解析器 HTML Parser

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

HTML文法定義 The HTML grammar definition

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

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

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

不幸的是,所有的傳統(tǒng)解析方式都不適用于html(當(dāng)然我提出它們并不只是因?yàn)楹猛?#xff0c;它們將用來解析css和js),html不能簡(jiǎn)單的用解析所需的上下文無(wú)關(guān)文法來定義。

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

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

HTML DTD

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

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

DOM

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

樹的根是“document”對(duì)象。

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

<html> <body> <p> Hello DOM </p> <div><img src=”example.png” /></div> </body> </html>

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

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

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

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

解析算法 The parsing algorithm

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

原因是:

1. 這門語(yǔ)言本身的寬容特性

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

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

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

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

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

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

圖9:HTML解析流程

符號(hào)識(shí)別算法 The tokenization algorithm

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

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

基本示例——符號(hào)化下面的html:

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

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

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

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

圖10:符號(hào)化示例輸入

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

瀏覽器容錯(cuò) Browsers error tolerance

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

以下面這段html為例:

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

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

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

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

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

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

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

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

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

下面來看一些webkit容錯(cuò)的例子:

</br>替代<br>

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

代碼:

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

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

迷路的表格

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

比如下面這個(gè)例子:

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

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

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

代碼:

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

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

嵌套的表單元素

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

代碼:

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

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

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

代碼:

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閉合標(biāo)簽

又一次不言自明。

支持不完整的html。我們從來不閉合body,因?yàn)橐恍┯薮赖木W(wǎng)頁(yè)總是在還未真正結(jié)束時(shí)就閉合它。我們依賴調(diào)用end方法去執(zhí)行關(guān)閉的處理。

代碼:

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

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

CSS解析 CSS parsing

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

看一些例子:

每個(gè)符號(hà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}*

“ident”是識(shí)別器的縮寫,相當(dāng)于一個(gè)class名,“name”是一個(gè)元素id(用“#”引用)。

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

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*] ‘)’ ] ;

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

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

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

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

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

Webkit CSS 解析器 Webkit CSS parser

Webkit使用Flex和Bison解析生成器從CSS語(yǔ)法文件中自動(dòng)生成解析器。回憶一下解析器的介紹,Bison創(chuàng)建一個(gè)自底向上的解析器,Firefox使用自頂向下解析器。它們都是將每個(gè)css文件解析為樣式表對(duì)象,每個(gè)對(duì)象包含css規(guī)則,css規(guī)則對(duì)象包含選擇器和聲明對(duì)象,以及其他一些符合css語(yǔ)法的對(duì)象。

圖12:解析css

腳本解析 Parsing scripts

本章將介紹Javascript。

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

腳本

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

預(yù)解析 Speculative parsing

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

樣式表 Style sheets

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

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

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

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

一個(gè)渲染對(duì)象直到怎么布局及繪制自己及它的children。

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

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 }

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

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; }

元素的類型也需要考慮,例如,表單控件和表格帶有特殊的框架。

在webkit中,如果一個(gè)元素想創(chuàng)建一個(gè)特殊的渲染對(duì)象,它需要復(fù)寫“createRenderer”方法,使渲染對(duì)象指向不包含幾何信息的樣式對(duì)象。

渲染樹和Dom樹的關(guān)系 The render tree relation to the DOM tree

渲染對(duì)象和Dom元素相對(duì)應(yīng),但這種對(duì)應(yīng)關(guān)系不是一對(duì)一的,不可見的Dom元素不會(huì)被插入渲染樹,例如head元素。另外,display屬性為none的元素也不會(huì)在渲染樹中出現(xiàn)(visibility屬性為hidden的元素將出現(xiàn)在渲染樹中)。

還有一些Dom元素對(duì)應(yīng)幾個(gè)可見對(duì)象,它們一般是一些具有復(fù)雜結(jié)構(gòu)的元素,無(wú)法用一個(gè)矩形來描述。例如,select元素有三個(gè)渲染對(duì)象——一個(gè)顯示區(qū)域、一個(gè)下拉列表及一個(gè)按鈕。同樣,當(dāng)文本因?yàn)閷挾炔粔蚨坌袝r(shí),新行將作為額外的渲染元素被添加。另一個(gè)多個(gè)渲染對(duì)象的例子是不規(guī)范的html,根據(jù)css規(guī)范,一個(gè)行內(nèi)元素只能僅包含行內(nèi)元素或僅包含塊狀元素,在存在混合內(nèi)容時(shí),將會(huì)創(chuàng)建匿名的塊狀渲染對(duì)象包裹住行內(nèi)元素。

一些渲染對(duì)象和所對(duì)應(yīng)的Dom節(jié)點(diǎn)不在樹上相同的位置,例如,浮動(dòng)和絕對(duì)定位的元素在文本流之外,在兩棵樹上的位置不同,渲染樹上標(biāo)識(shí)出真實(shí)的結(jié)構(gòu),并用一個(gè)占位結(jié)構(gòu)標(biāo)識(shí)出它們?cè)瓉淼奈恢谩?/p>

圖12:渲染樹及對(duì)應(yīng)的Dom樹

創(chuàng)建樹的流程 The flow of constructing the tree

Firefox中,表述為一個(gè)監(jiān)聽Dom更新的監(jiān)聽器,將frame的創(chuàng)建委派給Frame Constructor,這個(gè)構(gòu)建器計(jì)算樣式(參看樣式計(jì)算)并創(chuàng)建一個(gè)frame。

Webkit中,計(jì)算樣式并生成渲染對(duì)象的過程稱為attachment,每個(gè)Dom節(jié)點(diǎn)有一個(gè)attach方法,attachment的過程是同步的,調(diào)用新節(jié)點(diǎn)的attach方法將節(jié)點(diǎn)插入到Dom樹中。

處理html和body標(biāo)簽將構(gòu)建渲染樹的根,這個(gè)根渲染對(duì)象對(duì)應(yīng)被css規(guī)范稱為containing block的元素——包含了其他所有塊元素的頂級(jí)塊元素。它的大小就是viewport——瀏覽器窗口的顯示區(qū)域,Firefox稱它為viewPortFrame,webkit稱為RenderView,這個(gè)就是文檔所指向的渲染對(duì)象,樹中其他的部分都將作為一個(gè)插入的Dom節(jié)點(diǎn)被創(chuàng)建。

樣式計(jì)算 Style Computation

創(chuàng)建渲染樹需要計(jì)算出每個(gè)渲染對(duì)象的可視屬性,這可以通過計(jì)算每個(gè)元素的樣式屬性得到。

樣式包括各種來源的樣式表,行內(nèi)樣式元素及html中的可視化屬性(例如bgcolor),可視化屬性轉(zhuǎn)化為css樣式屬性。

樣式表來源于瀏覽器默認(rèn)樣式表,及頁(yè)面作者和用戶提供的樣式表——有些樣式是瀏覽器用戶提供的(瀏覽器允許用戶定義喜歡的樣式,例如,在Firefox中,可以通過在Firefox Profile目錄下放置樣式表實(shí)現(xiàn))。

計(jì)算樣式的一些困難:

1. 樣式數(shù)據(jù)是非常大的結(jié)構(gòu),保存大量的樣式屬性會(huì)帶來內(nèi)存問題

2. 如果不進(jìn)行優(yōu)化,找到每個(gè)元素匹配的規(guī)則會(huì)導(dǎo)致性能問題,為每個(gè)元素查找匹配的規(guī)則都需要遍歷整個(gè)規(guī)則表,這個(gè)過程有很大的工作量。選擇符可能有復(fù)雜的結(jié)構(gòu),匹配過程如果沿著一條開始看似正確,后來卻被證明是無(wú)用的路徑,則必須去嘗試另一條路徑。

例如,下面這個(gè)復(fù)雜選擇符

div div div div{…}

這意味著規(guī)則應(yīng)用到三個(gè)div的后代div元素,選擇樹上一條特定的路徑去檢查,這可能需要遍歷節(jié)點(diǎn)樹,最后卻發(fā)現(xiàn)它只是兩個(gè)div的后代,并不使用該規(guī)則,然后則需要沿著另一條路徑去嘗試

3. 應(yīng)用規(guī)則涉及非常復(fù)雜的級(jí)聯(lián),它們定義了規(guī)則的層次

我們來看一下瀏覽器如何處理這些問題:

共享樣式數(shù)據(jù)

webkit節(jié)點(diǎn)引用樣式對(duì)象(渲染樣式),某些情況下,這些對(duì)象可以被節(jié)點(diǎn)間共享,這些節(jié)點(diǎn)需要是兄弟或是表兄弟節(jié)點(diǎn),并且:

  • ▲這些元素必須處于相同的鼠標(biāo)狀態(tài)(比如不能一個(gè)處于hover,而另一個(gè)不是)
  • ▲不能有元素具有id
  • ▲標(biāo)簽名必須匹配
  • ▲class屬性必須匹配
  • ▲對(duì)應(yīng)的屬性必須相同
  • ▲鏈接狀態(tài)必須匹配
  • ▲焦點(diǎn)狀態(tài)必須匹配
  • ▲不能有元素被屬性選擇器影響
  • ▲元素不能有行內(nèi)樣式屬性
  • ▲不能有生效的兄弟選擇器,webcore在任何兄弟選擇器相遇時(shí)只是簡(jiǎn)單的拋出一個(gè)全局轉(zhuǎn)換,并且在它們顯示時(shí)使整個(gè)文檔的樣式共享失效,這些包括+選擇器和類似:first-child和:last-child這樣的選擇器。

Firefox規(guī)則樹 Firefox rule tree

Firefox用兩個(gè)樹用來簡(jiǎn)化樣式計(jì)算-規(guī)則樹和樣式上下文樹,webkit也有樣式對(duì)象,但它們并沒有存儲(chǔ)在類似樣式上下文樹這樣的樹中,只是由Dom節(jié)點(diǎn)指向其相關(guān)的樣式。

圖14:Firefox樣式上下文樹

樣式上下文包含最終值,這些值是通過以正確順序應(yīng)用所有匹配的規(guī)則,并將它們由邏輯值轉(zhuǎn)換為具體的值,例如,如果邏輯值為屏幕的百分比,則通過計(jì)算將其轉(zhuǎn)化為絕對(duì)單位。樣式樹的使用確實(shí)很巧妙,它使得在節(jié)點(diǎn)中共享的這些值不需要被多次計(jì)算,同時(shí)也節(jié)省了存儲(chǔ)空間。

所有匹配的規(guī)則都存儲(chǔ)在規(guī)則樹中,一條路徑中的底層節(jié)點(diǎn)擁有最高的優(yōu)先級(jí),這棵樹包含了所找到的 所有規(guī)則匹配的路徑(譯注:可以取巧理解為每條路徑對(duì)應(yīng)一個(gè)節(jié)點(diǎn),路徑上包含了該節(jié)點(diǎn)所匹配的所有規(guī)則)。規(guī)則樹并不是一開始就為所有節(jié)點(diǎn)進(jìn)行計(jì)算,而是 在某個(gè)節(jié)點(diǎn)需要計(jì)算樣式時(shí),才進(jìn)行相應(yīng)的計(jì)算并將計(jì)算后的路徑添加到樹中。

我們將樹上的路徑看成辭典中的單詞,假如已經(jīng)計(jì)算出了如下的規(guī)則樹:

假如需要為內(nèi)容樹中的另一個(gè)節(jié)點(diǎn)匹配規(guī)則,現(xiàn)在知道匹配的規(guī)則(以正確的順序)為B-E-I,因?yàn)槲覀円呀?jīng)計(jì)算出了路徑A-B-E-I-L,所以樹上已經(jīng)存在了這條路徑,剩下的工作就很少了。

現(xiàn)在來看一下樹如何保存。

結(jié)構(gòu)化

樣式上下文按結(jié)構(gòu)劃分,這些結(jié)構(gòu)包括類似border或color這樣的特定分類的樣式信息。一個(gè)結(jié)構(gòu)中的所有特性不是繼承的就是非繼承的,對(duì)繼承的特性,除非元素自身有定義,否則就從它的parent繼承。非繼承的特性(稱為reset特性)如果沒有定義,則使用默認(rèn)的值。

樣式上下文樹緩存完整的結(jié)構(gòu)(包括計(jì)算后的值),這樣,如果底層節(jié)點(diǎn)沒有為一個(gè)結(jié)構(gòu)提供定義,則使用上層節(jié)點(diǎn)緩存的結(jié)構(gòu)。

使用規(guī)則樹計(jì)算樣式上下文

當(dāng)為一個(gè)特定的元素計(jì)算樣式時(shí),首先計(jì)算出規(guī)則樹中的一條路徑,或是使用已經(jīng)存在的一條,然后使 用路徑中的規(guī)則去填充新的樣式上下文,從樣式的底層節(jié)點(diǎn)開始,它具有最高優(yōu)先級(jí)(通常是最特定的選擇器),遍歷規(guī)則樹,直到填滿結(jié)構(gòu)。如果在那個(gè)規(guī)則節(jié)點(diǎn) 沒有定義所需的結(jié)構(gòu)規(guī)則,則沿著路徑向上,直到找到該結(jié)構(gòu)規(guī)則。

如果最終沒有找到該結(jié)構(gòu)的任何規(guī)則定義,那么如果這個(gè)結(jié)構(gòu)是繼承型的,則找到其在內(nèi)容樹中的parent的結(jié)構(gòu),這種情況下,我們也成功的共享了結(jié)構(gòu);如果這個(gè)結(jié)構(gòu)是reset型的,則使用默認(rèn)的值。

如果特定的節(jié)點(diǎn)添加了值,那么需要做一些額外的計(jì)算以將其轉(zhuǎn)換為實(shí)際值,然后在樹上的節(jié)點(diǎn)緩存該值,使它的children可以使用。

當(dāng)一個(gè)元素和它的一個(gè)兄弟元素指向同一個(gè)樹節(jié)點(diǎn)時(shí),完整的樣式上下文可以被它們共享。

來看一個(gè)例子:假設(shè)有下面這段html

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

以及下面這些規(guī)則

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

簡(jiǎn)化下問題,我們只填充兩個(gè)結(jié)構(gòu)——color和margin,color結(jié)構(gòu)只包含一個(gè)成員-顏色,margin結(jié)構(gòu)包含四邊。

生成的規(guī)則樹如下(節(jié)點(diǎn)名:指向的規(guī)則)

上下文樹如下(節(jié)點(diǎn)名:指向的規(guī)則節(jié)點(diǎn))

假設(shè)我們解析html,遇到第二個(gè)div標(biāo)簽,我們需要為這個(gè)節(jié)點(diǎn)創(chuàng)建樣式上下文,并填充它的樣式結(jié)構(gòu)。

我們進(jìn)行規(guī)則匹配,找到這個(gè)div匹配的規(guī)則為1、2、6,我們發(fā)現(xiàn)規(guī)則樹上已經(jīng)存在了一條我們可以使用的路徑1、2,我們只需為規(guī)則6新增一個(gè)節(jié)點(diǎn)添加到下面(就是規(guī)則樹中的F)。

然后創(chuàng)建一個(gè)樣式上下文并將其放到上下文樹中,新的樣式上下文將指向規(guī)則樹中的節(jié)點(diǎn)F。

現(xiàn)在我們需要填充這個(gè)樣式上下文,先從填充margin結(jié)構(gòu)開始,既然最后一個(gè)規(guī)則節(jié)點(diǎn)沒有添加margin結(jié)構(gòu),沿著路徑向上,直到找到緩存的前面插入節(jié)點(diǎn)計(jì)算出的結(jié)構(gòu),我們發(fā)現(xiàn)B是最近的指定margin值的節(jié)點(diǎn)。因?yàn)橐呀?jīng)有了color結(jié)構(gòu)的定義,所以不能使用緩存的結(jié)構(gòu),既然color只有一個(gè)屬性,也就不需要沿著路徑向上填充其他屬性。計(jì)算出最終值(將字符串轉(zhuǎn)換為RGB等),并緩存計(jì)算后的結(jié)構(gòu)。

第二個(gè)span元素更簡(jiǎn)單,進(jìn)行規(guī)則匹配后發(fā)現(xiàn)它指向規(guī)則G,和前一個(gè)span一樣,既然有兄弟節(jié)點(diǎn)指向同一個(gè)節(jié)點(diǎn),就可以共享完整的樣式上下文,只需指向前一個(gè)span的上下文。

因?yàn)榻Y(jié)構(gòu)中包含繼承自parent的規(guī)則,上下文樹做了緩存(color特性是繼承來的,但Firefox將其視為reset并在規(guī)則樹中緩存)。

例如,如果我們?yōu)橐粋€(gè)paragraph的文字添加規(guī)則:

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

那么這個(gè)p在內(nèi)容樹中的子節(jié)點(diǎn)div,會(huì)共享和它parent一樣的font結(jié)構(gòu),這種情況發(fā)生在沒有為這個(gè)div指定font規(guī)則時(shí)。

Webkit中,并沒有規(guī)則樹,匹配的聲明會(huì)被遍歷四次,先是應(yīng)用非important的高優(yōu)先級(jí)屬性(之所以先應(yīng)用這些屬性,是因?yàn)槠渌囊蕾囉谒鼈?#xff0d;比如display),其次是高優(yōu)先級(jí)important的,接著是一般優(yōu)先級(jí)非important的,最后是一般優(yōu)先級(jí)important的規(guī)則。這樣,出現(xiàn)多次的屬性將被按照正確的級(jí)聯(lián)順序進(jìn)行處理,最后一個(gè)生效。

總結(jié)一下,共享樣式對(duì)象(結(jié)構(gòu)中完整或部分內(nèi)容)解決了問題1和3,Firefox的規(guī)則樹幫助以正確的順序應(yīng)用規(guī)則。

對(duì)規(guī)則進(jìn)行處理以簡(jiǎn)化匹配過程

樣式規(guī)則有幾個(gè)來源:

· 外部樣式表或style標(biāo)簽內(nèi)的css規(guī)則

· 行內(nèi)樣式屬性

· html可視化屬性(映射為相應(yīng)的樣式規(guī)則)

后面兩個(gè)很容易匹配到元素,因?yàn)樗鼈兯鶕碛械臉邮綄傩院蚳tml屬性可以將元素作為key進(jìn)行映射。

就像前面問題2所提到的,css的規(guī)則匹配可能很狡猾,為了解決這個(gè)問題,可以先對(duì)規(guī)則進(jìn)行處理,以使其更容易被訪問。

解析完樣式表之后,規(guī)則會(huì)根據(jù)選擇符添加一些hash映射,映射可以是根據(jù)id、class、標(biāo)簽名或是任何不屬于這些分類的綜合映射。如果選擇符為id,規(guī)則將被添加到id映射,如果是class,則被添加到class映射,等等。

這個(gè)處理是匹配規(guī)則更容易,不需要查看每個(gè)聲明,我們能從映射中找到一個(gè)元素的相關(guān)規(guī)則,這個(gè)優(yōu)化使在進(jìn)行規(guī)則匹配時(shí)減少了95+%的工作量。

來看下面的樣式規(guī)則:

p.error {color:red}

#messageDiv {height:50px}

div {margin:5px}

第一條規(guī)則將被插入class映射,第二條插入id映射,第三條是標(biāo)簽映射。

下面這個(gè)html片段:

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

我們首先找到p元素對(duì)應(yīng)的規(guī)則,class映射將包含一個(gè)“error”的key,找到p.error的規(guī)則,div在id映射和標(biāo)簽映射中都有相關(guān)的規(guī)則,剩下的工作就是找出這些由key對(duì)應(yīng)的規(guī)則中哪些確實(shí)是正確匹配的。

例如,如果div的規(guī)則是

table div {margin:5px}

這也是標(biāo)簽映射產(chǎn)生的,因?yàn)閗ey是最右邊的選擇符,但它并不匹配這里的div元素,因?yàn)檫@里的div沒有table祖先。

Webkit和Firefox都會(huì)做這個(gè)處理。

以正確的級(jí)聯(lián)順序應(yīng)用規(guī)則

樣式對(duì)象擁有對(duì)應(yīng)所有可見屬性的屬性,如果特性沒有被任何匹配的規(guī)則所定義,那么一些特性可以從parent的樣式對(duì)象中繼承,另外一些使用默認(rèn)值。

這個(gè)問題的產(chǎn)生是因?yàn)榇嬖诓恢挂惶幍亩x,這里用級(jí)聯(lián)順序解決這個(gè)問題。

樣式表的級(jí)聯(lián)順序

一個(gè)樣式屬性的聲明可能在幾個(gè)樣式表中出現(xiàn),或是在一個(gè)樣式表中出現(xiàn)多次,因此,應(yīng)用規(guī)則的順序至關(guān)重要,這個(gè)順序就是級(jí)聯(lián)順序。根據(jù)css2的規(guī)范,級(jí)聯(lián)順序?yàn)?#xff08;從低到高):

  • 1. 瀏覽器聲明
  • 2. 用戶聲明
  • 3. 作者的一般聲明
  • 4. 作者的important聲明
  • 5. 用戶important聲明

瀏覽器聲明是最不重要的,用戶只有在聲明被標(biāo)記為important時(shí)才會(huì)覆蓋作者的聲明。具有同等級(jí)別的聲明將根據(jù)specifity以及它們被定義時(shí)的順序進(jìn)行排序。Html可視化屬性將被轉(zhuǎn)換為匹配的css聲明,它們被視為最低優(yōu)先級(jí)的作者規(guī)則。

Specifity

Css2規(guī)范中定義的選擇符specifity如下:

  • · 如果聲明來自style屬性,而不是一個(gè)選擇器的規(guī)則,則計(jì)1,否則計(jì)0(=a)
  • · 計(jì)算選擇器中id屬性的數(shù)量(=b)
  • · 計(jì)算選擇器中class及偽類的數(shù)量(=c)
  • · 計(jì)算選擇器中元素名及偽元素的數(shù)量(=d)

連接a-b-c-d四個(gè)數(shù)量(用一個(gè)大基數(shù)的計(jì)算系統(tǒng))將得到specifity。這里使用的基數(shù)由分類中最高的基數(shù)定義。例如,如果a為14,可以使用16進(jìn)制。不同情況下,a為17時(shí),則需要使用阿拉伯?dāng)?shù)字17作為基數(shù),這種情況可能在這個(gè)選擇符時(shí)發(fā)生html body div div …(選擇符中有17個(gè)標(biāo)簽,一般不太可能)。

一些例子:

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

規(guī)則排序

規(guī)則匹配后,需要根據(jù)級(jí)聯(lián)順序?qū)σ?guī)則進(jìn)行排序,webkit先將小列表用冒泡排序,再將它們合并為一個(gè)大列表,webkit通過為規(guī)則復(fù)寫“>”操作來執(zhí)行排序:

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; }

逐步處理 Gradual process

webkit使用一個(gè)標(biāo)志位標(biāo)識(shí)所有頂層樣式表都已加載,如果在attch時(shí)樣式?jīng)]有完全加載,則放置占位符,并在文檔中標(biāo)記,一旦樣式表完成加載就重新進(jìn)行計(jì)算。

布局 Layout

當(dāng)渲染對(duì)象被創(chuàng)建并添加到樹中,它們并沒有位置和大小,計(jì)算這些值的過程稱為layout或reflow。

Html使用基于流的布局模型,意味著大部分時(shí)間,可以以單一的途徑進(jìn)行幾何計(jì)算。流中靠后的元素并不會(huì)影響前面元素的幾何特性,所以布局可以在文檔中從右向左、自上而下的進(jìn)行。也存在一些例外,比如html tables。

坐標(biāo)系統(tǒng)相對(duì)于根frame,使用top和left坐標(biāo)。

布局是一個(gè)遞歸的過程,由根渲染對(duì)象開始,它對(duì)應(yīng)html文檔元素,布局繼續(xù)遞歸的通過一些或所有的frame層級(jí),為每個(gè)需要幾何信息的渲染對(duì)象進(jìn)行計(jì)算。

根渲染對(duì)象的位置是0,0,它的大小是viewport-瀏覽器窗口的可見部分。

所有的渲染對(duì)象都有一個(gè)layout或reflow方法,每個(gè)渲染對(duì)象調(diào)用需要布局的children的layout方法。

Dirty bit 系統(tǒng)

為了不因?yàn)槊總€(gè)小變化都全部重新布局,瀏覽器使用一個(gè)dirty bit系統(tǒng),一個(gè)渲染對(duì)象發(fā)生了變化或是被添加了,就標(biāo)記它及它的children為dirty-需要layout。存在兩個(gè)標(biāo)識(shí)-dirty及children are dirty,children are dirty說明即使這個(gè)渲染對(duì)象可能沒問題,但它至少有一個(gè)child需要layout。

全局和增量 layout

當(dāng)layout在整棵渲染樹觸發(fā)時(shí),稱為全局layout,這可能在下面這些情況下發(fā)生:

1. 一個(gè)全局的樣式改變影響所有的渲染對(duì)象,比如字號(hào)的改變

2. 窗口resize

layout也可以是增量的,這樣只有標(biāo)志為dirty的渲染對(duì)象會(huì)重新布局(也將導(dǎo)致一些額外的布局)。增量 layout會(huì)在渲染對(duì)象dirty時(shí)異步觸發(fā),例如,當(dāng)網(wǎng)絡(luò)接收到新的內(nèi)容并添加到Dom樹后,新的渲染對(duì)象會(huì)添加到渲染樹中。

圖20:增量 layout

異步和同步layout

增量layout的過程是異步的,Firefox為增量layout生成了reflow隊(duì)列,以及一個(gè)調(diào)度執(zhí)行這些批處理命令。Webkit也有一個(gè)計(jì)時(shí)器用來執(zhí)行增量layout-遍歷樹,為dirty狀態(tài)的渲染對(duì)象重新布局。

另外,當(dāng)腳本請(qǐng)求樣式信息時(shí),例如“offsetHeight”,會(huì)同步的觸發(fā)增量布局。

全局的layout一般都是同步觸發(fā)。

有些時(shí)候,layout會(huì)被作為一個(gè)初始layout之后的回調(diào),比如滑動(dòng)條的滑動(dòng)。

優(yōu)化

當(dāng)一個(gè)layout因?yàn)閞esize或是渲染位置改變(并不是大小改變)而觸發(fā)時(shí),渲染對(duì)象的大小將會(huì)從緩存中讀取,而不會(huì)重新計(jì)算。

一般情況下,如果只有子樹發(fā)生改變,則layout并不從根開始。這種情況發(fā)生在,變化發(fā)生在元素自身并且不影響它周圍元素,例如,將文本插入文本域(否則,每次擊鍵都將觸發(fā)從根開始的重排)。

layout過程

layout一般有下面這幾個(gè)部分:

1. parent渲染對(duì)象決定它的寬度

2. parent渲染對(duì)象讀取chilidren,并:

1. 放置child渲染對(duì)象(設(shè)置它的x和y)

2. 在需要時(shí)(它們當(dāng)前為dirty或是處于全局layout或者其他原因)調(diào)用child渲染對(duì)象的layout,這將計(jì)算child的高度

3. parent渲染對(duì)象使用child渲染對(duì)象的累積高度,以及margin和padding的高度來設(shè)置自己的高度-這將被parent渲染對(duì)象的parent使用

4. 將dirty標(biāo)識(shí)設(shè)置為false

Firefox使用一個(gè)“state”對(duì)象(nsHTMLReflowState)做為參數(shù)去布局(firefox稱為reflow),state包含parent的寬度及其他內(nèi)容。

Firefox布局的輸出是一個(gè)“metrics”對(duì)象(nsHTMLReflowMetrics)。它包括渲染對(duì)象計(jì)算出的高度。

寬度計(jì)算

渲染對(duì)象的寬度使用容器的寬度、渲染對(duì)象樣式中的寬度及margin、border進(jìn)行計(jì)算。例如,下面這個(gè)div的寬度:

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

webkit中寬度的計(jì)算過程是(RenderBox類的calcWidth方法):

· 容器的寬度是容器的可用寬度和0中的最大值,這里的可用寬度為:contentWidth=clientWidth()-paddingLeft()-paddingRight(),clientWidth和clientHeight代表一個(gè)對(duì)象內(nèi)部的不包括border和滑動(dòng)條的大小

· 元素的寬度指樣式屬性width的值,它可以通過計(jì)算容器的百分比得到一個(gè)絕對(duì)值

· 加上水平方向上的border和padding

到這里是最佳寬度的計(jì)算過程,現(xiàn)在計(jì)算寬度的最大值和最小值,如果最佳寬度大于最大寬度則使用最大寬度,如果小于最小寬度則使用最小寬度。最后緩存這個(gè)值,當(dāng)需要layout但寬度未改變時(shí)使用。

Line breaking

當(dāng)一個(gè)渲染對(duì)象在布局過程中需要折行時(shí),則暫停并告訴它的parent它需要折行,parent將創(chuàng)建額外的渲染對(duì)象并調(diào)用它們的layout。

繪制 Painting

繪制階段,遍歷渲染樹并調(diào)用渲染對(duì)象的paint方法將它們的內(nèi)容顯示在屏幕上,繪制使用UI基礎(chǔ)組件,這在UI的章節(jié)有更多的介紹。

全局和增量

和布局一樣,繪制也可以是全局的-繪制完整的樹-或增量的。在增量的繪制過程中,一些渲染對(duì)象以不影響整棵樹的方式改變,改變的渲染對(duì)象使其在屏幕上的矩形區(qū)域失效,這將導(dǎo)致操作系統(tǒng)將其看作dirty區(qū)域,并產(chǎn)生一個(gè)paint事件,操作系統(tǒng)很巧妙的處理這個(gè)過程,并將多個(gè)區(qū)域合并為一個(gè)。Chrome中,這個(gè)過程更復(fù)雜些,因?yàn)殇秩緦?duì)象在不同的進(jìn)程中,而不是在主進(jìn)程中。Chrome在一定程度上模擬操作系統(tǒng)的行為,表現(xiàn)為監(jiān)聽事件并派發(fā)消息給渲染根,在樹中查找到相關(guān)的渲染對(duì)象,重繪這個(gè)對(duì)象(往往還包括它的children)。

繪制順序

css2定義了繪制過程的順序-www.w3.org/TR/CSS21/zi…。這個(gè)就是元素壓入堆棧的順序,這個(gè)順序影響著繪制,堆棧從后向前進(jìn)行繪制。

一個(gè)塊渲染對(duì)象的堆棧順序是:

  • 1. 背景色
  • 2. 背景圖
  • 3. border
  • 4. children
  • 5. outline

Firefox顯示列表

Firefox讀取渲染樹并為繪制的矩形創(chuàng)建一個(gè)顯示列表,該列表以正確的繪制順序包含這個(gè)矩形相關(guān)的渲染對(duì)象。

用這樣的方法,可以使重繪時(shí)只需查找一次樹,而不需要多次查找——繪制所有的背景、所有的圖片、所有的border等等。

Firefox優(yōu)化了這個(gè)過程,它不添加會(huì)被隱藏的元素,比如元素完全在其他不透明元素下面。

Webkit矩形存儲(chǔ)

重繪前,webkit將舊的矩形保存為位圖,然后只繪制新舊矩形的差集。

動(dòng)態(tài)變化

瀏覽器總是試著以最小的動(dòng)作響應(yīng)一個(gè)變化,所以一個(gè)元素顏色的變化將只導(dǎo)致該元素的重繪,元素位置的變化將大致元素的布局和重繪,添加一個(gè)Dom節(jié)點(diǎn),也會(huì)大致這個(gè)元素的布局和重繪。一些主要的變化,比如增加html元素的字號(hào),將會(huì)導(dǎo)致緩存失效,從而引起整數(shù)的布局和重繪。

渲染引擎的線程

渲染引擎是單線程的,除了網(wǎng)絡(luò)操作以外,幾乎所有的事情都在單一的線程中處理,在Firefox和Safari中,這是瀏覽器的主線程,Chrome中這是tab的主線程。

網(wǎng)絡(luò)操作由幾個(gè)并行線程執(zhí)行,并行連接的個(gè)數(shù)是受限的(通常是2-6個(gè))。

事件循環(huán)

瀏覽器主線程是一個(gè)事件循環(huán),它被設(shè)計(jì)為無(wú)限循環(huán)以保持執(zhí)行過程的可用,等待事件(例如layout和paint事件)并執(zhí)行它們。下面是Firefox的主要事件循環(huán)代碼。

while (!mExiting) NS_ProcessNextEvent(thread);

CSS2 可視模型 CSS2 visual module

畫布 The Canvas

根據(jù)CSS2規(guī)范,術(shù)語(yǔ)canvas用來描述格式化的結(jié)構(gòu)所渲染的空間——瀏覽器繪制內(nèi)容的地方。畫布對(duì)每個(gè)維度空間都是無(wú)限大的,但瀏覽器基于viewport的大小選擇了一個(gè)初始寬度。

根據(jù)www.w3.org/TR/CSS2/zin…的定義,畫布如果是包含在其他畫布內(nèi)則是透明的,否則瀏覽器會(huì)指定一個(gè)顏色。

CSS盒模型

CSS盒模型描述了矩形盒,這些矩形盒是為文檔樹中的元素生成的,并根據(jù)可視的格式化模型進(jìn)行布局。每個(gè)box包括內(nèi)容區(qū)域(如圖片、文本等)及可選的四周padding、border和margin區(qū)域。

每個(gè)節(jié)點(diǎn)生成0-n個(gè)這樣的box。

所有的元素都有一個(gè)display屬性,用來決定它們生成box的類型,例如:

block-生成塊狀box

inline-生成一個(gè)或多個(gè)行內(nèi)box

none-不生成box

默認(rèn)的是inline,但瀏覽器樣式表設(shè)置了其他默認(rèn)值,例如,div元素默認(rèn)為block。可以訪問www.w3.org/TR/CSS2/sam…查看更多的默認(rèn)樣式表示例。

定位策略 Position scheme

這里有三種策略:

1. normal-對(duì)象根據(jù)它在文檔的中位置定位,這意味著它在渲染樹和在Dom樹中位置一致,并根據(jù)它的盒模型和大小進(jìn)行布局

2. float-對(duì)象先像普通流一樣布局,然后盡可能的向左或是向右移動(dòng)

3. absolute-對(duì)象在渲染樹中的位置和Dom樹中位置無(wú)關(guān)

static和relative是normal,absolute和fixed屬于absolute。

在static定位中,不定義位置而使用默認(rèn)的位置。其他策略中,作者指定位置——top、bottom、left、right。

Box布局的方式由這幾項(xiàng)決定:box的類型、box的大小、定位策略及擴(kuò)展信息(比如圖片大小和屏幕尺寸)。

Box類型

Block box:構(gòu)成一個(gè)塊,即在瀏覽器窗口上有自己的矩形

Inline box:并沒有自己的塊狀區(qū)域,但包含在一個(gè)塊狀區(qū)域內(nèi)

block一個(gè)挨著一個(gè)垂直格式化,inline則在水平方向上格式化。

Inline盒模型放置在行內(nèi)或是line box中,每行至少和最高的box一樣高,當(dāng)box以baseline對(duì)齊時(shí)——即一個(gè)元素的底部和另一個(gè)box上除底部以外的某點(diǎn)對(duì)齊,行高可以比最高的box高。當(dāng)容器寬度不夠時(shí),行內(nèi)元素將被放到多行中,這在一個(gè)p元素中經(jīng)常發(fā)生。

定位 Position

Relative

相對(duì)定位——先按照一般的定位,然后按所要求的差值移動(dòng)。

Floats

一個(gè)浮動(dòng)的box移動(dòng)到一行的最左邊或是最右邊,其余的box圍繞在它周圍。下面這段html:

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

將顯示為:

Absolute和Fixed

這種情況下的布局完全不顧普通的文檔流,元素不屬于文檔流的一部分,大小取決于容器。Fixed時(shí),容器為viewport(可視區(qū)域)。

圖17:fixed

注意-fixed即使在文檔流滾動(dòng)時(shí)也不會(huì)移動(dòng)。

Layered representation

這個(gè)由CSS屬性中的z-index指定,表示盒模型的第三個(gè)大小,即在z軸上的位置。Box分發(fā)到堆棧中(稱為堆棧上下文),每個(gè)堆棧中靠后的元素將被較早繪制,棧頂靠前的元素離用戶最近,當(dāng)發(fā)生交疊時(shí),將隱藏靠后的元素。堆棧根據(jù)z-index屬性排序,擁有z-index屬性的box形成了一個(gè)局部堆棧,viewport有外部堆棧,例如:

<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>

結(jié)果是:


雖然綠色div排在紅色div后面,可能在正常流中也已經(jīng)被繪制在后面,但z-index有更高優(yōu)先級(jí),所以在根box的堆棧中更靠前。

國(guó)外也有網(wǎng)友根據(jù)瀏覽器的工作原理繪制了幾張工作流程圖,方便大家通過簡(jiǎn)易的圖片來了解這個(gè)辛苦的過程:


原文發(fā)布時(shí)間:2018-06-26

原文作者:zzzaquarius

本文來源掘金如需轉(zhuǎn)載請(qǐng)緊急聯(lián)系作者

總結(jié)

以上是生活随笔為你收集整理的现代浏览器的工作原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

久久99国产精品自在自在app | 黄色一级大片在线免费看国产一 | 免费精品视频在线 | 综合天堂av久久久久久久 | 久久久精品国产免费观看一区二区 | 国产一区二区久久久 | 色婷婷久久一区二区 | 在线观看视频一区二区三区 | 国产91国语对白在线 | 视频在线观看日韩 | a级片韩国 | 日韩av在线免费播放 | 玖玖国产精品视频 | 国产精品福利午夜在线观看 | 在线成人免费 | 日韩av电影免费在线观看 | 国产麻豆剧传媒免费观看 | 久久综合精品一区 | 精品久久久一区二区 | 激情视频久久 | 午夜av在线播放 | 久久免费在线观看视频 | 国产久草在线观看 | 国产不卡毛片 | 日韩黄色免费看 | 看v片 | 日韩理论电影网 | 美女免费视频黄 | 色婷婷精品大在线视频 | 国产一级免费av | 成人资源网 | 精品一区 在线 | 国产视频精选在线 | 一区二区伦理 | 天天操天天摸天天射 | 一区二区三区四区在线 | 美女久久久久久久 | 中文字幕在线不卡国产视频 | 精品美女国产在线 | 91伊人久久大香线蕉蜜芽人口 | av手机在线播放 | 91福利社在线观看 | 国产在线p| 国产精品k频道 | 欧美国产91 | 久久久这里有精品 | 伊甸园av在线 | 亚洲va在线va天堂 | 337p日本大胆噜噜噜噜 | 久久成人免费视频 | 天天拍天天爽 | 久久任你操 | 91精品资源| 中文字幕电影在线 | 久久精品国亚洲 | 亚洲a网| 日韩精品中文字幕在线 | 日本性生活一级片 | 久草视频资源 | 国产成人a亚洲精品v | 日韩乱色精品一区二区 | 丝袜美腿亚洲综合 | 四虎国产视频 | 欧美成人在线网站 | 又黄又刺激视频 | 国产精品麻豆免费版 | 国产精品视频app | 国产精品成人免费精品自在线观看 | 精品久久久影院 | 日韩精选在线观看 | 射久久久| 成人免费在线视频观看 | 亚洲精品视频免费看 | 不卡的av电影在线观看 | 热精品 | 91人人在线 | 中文字幕丰满人伦在线 | 国产一级淫片免费看 | 91精品国产99久久久久 | 日日干夜夜爱 | 久久久久久久久久久免费 | 1区2区3区在线观看 三级动图 | 国产亚洲永久域名 | 97av在线视频免费播放 | 婷婷亚洲最大 | 男女激情片在线观看 | 免费福利视频网站 | 四虎成人精品永久免费av | 久久久国产一区 | 国产精品一区专区欧美日韩 | 国产精品系列在线 | 91亚洲免费| a天堂在线看| 国产精品高潮呻吟久久久久 | 在线观看黄污 | 久草在线在线视频 | 又大又硬又黄又爽视频在线观看 | 涩av在线| 国产一区在线视频 | 69国产精品视频 | 国产精品久久久99 | 欧美性成人 | 国产黄色在线观看 | 国内外成人在线 | 黄色国产在线观看 | 国产在线免费 | 97视频在线观看网址 | 久久夜av | 麻豆一精品传二传媒短视频 | 国产麻豆精品一区二区 | 国产精品久久久av久久久 | 精品欧美在线视频 | 成人97视频一区二区 | 国产精品成人av久久 | www国产亚洲精品久久网站 | 国产一区欧美日韩 | 亚洲成熟女人毛片在线 | 天天操天天怕 | 97视频在线观看网址 | 亚洲欧洲成人 | 999久久国产 | 韩国一区二区三区在线观看 | 国产成人精品在线 | 亚洲免费观看在线视频 | 91黄色免费网站 | 一区二区视频网站 | 成人在线免费看 | 色综合久久88色综合天天免费 | 麻豆久久一区二区 | 最新在线你懂的 | 韩日精品在线 | av在线免费在线观看 | 人人射av | 日本在线成人 | 人人澡人人草 | 日本精品视频免费观看 | 在线观看中文字幕 | 亚洲欧美成aⅴ人在线观看 四虎在线观看 | 国产在线观看高清视频 | 日韩av福利在线 | 99九九99九九九视频精品 | 天堂va在线高清一区 | 青青草久草在线 | 久草视频在线资源站 | 久草在线视频在线观看 | 亚洲国产日韩精品 | 国产精品久久久久久久久久免费 | 久久精品一区 | 日本色小说视频 | 91精品国产一区二区在线观看 | 成全免费观看视频 | 黄色免费观看视频 | 狠狠色丁香婷婷综合 | 日韩欧美高清免费 | 日本少妇高清做爰视频 | 亚洲精品国产精品国 | 亚州免费视频 | 国产亚洲精品福利 | 国产精品久久久久一区 | 91成人免费看 | 精品国产精品国产偷麻豆 | 国产视频 亚洲视频 | 色资源在线 | 国产精品成人一区二区三区 | 久久这里只有精品1 | 九九视频网| 麻豆视频在线免费观看 | 在线观看第一页 | 69夜色精品国产69乱 | 久久夜色精品国产欧美一区麻豆 | 人人插人人搞 | 国产日韩中文字幕在线 | 国产色久| 成人免费观看视频网站 | 99精品在线看| 日韩高清激情 | 久久99热久久99精品 | 一区二区三区四区精品 | 日韩av免费一区 | www免费| 免费观看的黄色片 | 美女免费视频网站 | 国产69久久精品成人看 | 最近2019好看的中文字幕免费 | www.色综合.com | 日韩三级视频在线观看 | 国产精品久久久视频 | 香蕉97视频观看在线观看 | 国产美女在线精品免费观看 | 在线播放 日韩专区 | 99久久久国产精品免费99 | 在线看中文字幕 | 在线视频 成人 | 亚洲精品国产精品国自产观看 | 国产欧美日韩精品一区二区免费 | 亚洲视频综合在线 | 国产又粗又长又硬免费视频 | 国产精品一区二区在线 | 欧美成年黄网站色视频 | 日韩欧美视频在线播放 | 国产中文字幕在线看 | 欧美日韩精品在线视频 | 黄在线免费看 | 精品在线免费观看 | 黄av资源| 五月天亚洲综合 | 精品国产诱惑 | 又色又爽的网站 | 日韩久久午夜一级啪啪 | 一区二区三区免费在线 | 亚洲日本在线视频观看 | 免费av大全 | 免费的黄色av | 2019中文最近的2019中文在线 | 96久久精品 | 视频在线亚洲 | 五月婷婷激情六月 | 国产成人一区二区三区免费看 | 99精品国产成人一区二区 | 狠狠色狠狠综合久久 | 中文字幕在线播放一区二区 | 国产无套精品久久久久久 | 国产精品久久久久四虎 | 99视频导航| 久久久久免费精品视频 | 日本 在线 视频 中文 有码 | 国产精品一区二区免费视频 | 日韩欧美电影在线 | 视频一区二区三区视频 | 手机看片99 | a级成人毛片 | www.91成人| 国产精品男女视频 | 91亚洲精品久久久蜜桃网站 | 一级黄色片在线播放 | 国产99亚洲 | 久久99在线 | 综合久久久久久久久 | 日韩免费大片 | 手机看片1042 | 亚洲影视资源 | 91九色成人蝌蚪首页 | 久久国产一区二区 | 超碰人人91 | 欧美国产日韩中文 | 久久久久久激情 | 国内精品在线观看视频 | 91精选在线观看 | 伊人开心激情 | 国产精品永久在线 | 久精品视频在线观看 | 亚洲经典中文字幕 | 五月情婷婷 | 国产女人免费看a级丨片 | 午夜av一区| 久久久久亚洲精品中文字幕 | 99re国产| 国产一二区视频 | 国产美女网| 在线免费看片 | 中文字幕视频在线播放 | 亚洲国产日韩一区 | 亚洲黄色免费网站 | 中国一级特黄毛片大片久久 | 成人免费共享视频 | 日日夜夜精品 | 欧美美女一级片 | 成人禁用看黄a在线 | 国产成人一区二区三区 | 免费久久片 | 久久99日韩| 精品久操| 国产高清不卡av | 在线影视 一区 二区 三区 | 婷婷在线网站 | 日韩电影一区二区三区 | 日韩欧美国产视频 | 国产色视频网站 | 久久99精品久久久久久久久久久久 | 深夜激情影院 | 日韩专区中文字幕 | 日韩免费电影一区二区三区 | 日韩乱理 | 久久久精品在线观看 | 日韩午夜视频在线观看 | 中文字幕在线观看1 | 日韩精品欧美精品 | 国产成人免费av电影 | 麻豆成人网 | 一区二区三区免费网站 | 不卡精品视频 | 在线观看岛国 | 久草精品电影 | 久久99亚洲精品久久 | 亚洲乱亚洲乱妇 | 国产精品一二 | 超碰com| 在线黄频| 97超碰在线久草超碰在线观看 | 91激情| 色视频网站免费观看 | 欧美性爽爽 | 精品视频 | 亚洲精品在线国产 | 久久久黄视频 | 日本女人逼 | 国产一级片在线播放 | 国产最新精品视频 | 五月天六月色 | 国产高清区 | 成人h视频在线播放 | 日韩专区一区二区 | 91网站在线视频 | 97久久精品午夜一区二区 | 久久久久久久av | 国产大片黄色 | 中文字幕亚洲综合久久五月天色无吗'' | 久久视频在线免费观看 | 中文字幕精品一区二区三区电影 | 亚洲精品国 | 在线观看国产高清视频 | 欧美巨乳网| 国产三级精品在线 | 一区中文字幕在线观看 | 精品一区二区视频 | 97电影手机版 | 日韩天天综合 | 国产精品久久久久久电影 | 在线观看久久久久久 | 久久精品国产亚洲a | 国产在线a视频 | 亚洲专区视频在线观看 | 成人久久综合 | 中文字幕在线观看一区二区 | 免费热情视频 | 最新国产精品亚洲 | 国产日韩精品一区二区三区在线 | 久久久高清一区二区三区 | 超碰av在线播放 | 蜜桃av人人夜夜澡人人爽 | .国产精品成人自产拍在线观看6 | 91亚洲精品乱码久久久久久蜜桃 | 国产成人精品一区一区一区 | 视频二区在线 | 在线www色| 狠狠躁夜夜躁人人爽超碰97香蕉 | 欧美日韩p片 | 日韩欧美精品一区 | 在线观看久久 | 91av视频在线观看免费 | 久久综合五月天 | 一级免费av| 99精品视频免费观看视频 | 韩日av一区二区 | 国产精品久久久久永久免费 | 午夜精品视频一区二区三区在线看 | 亚洲国产成人高清精品 | 日韩高清dvd | 精品一二三区 | 香蕉视频色 | 亚洲电影成人 | 一区二区三区在线观看免费 | 免费高清无人区完整版 | 麻豆 videos | 久久人人爽av| 欧美日本日韩aⅴ在线视频 插插插色综合 | 欧美一级大片在线观看 | 国产在线永久 | 伊人资源视频在线 | 亚洲黄色免费电影 | 国产一级性生活视频 | 亚洲精品久久久久中文字幕二区 | 国产激情电影综合在线看 | 人操人| 爱爱av在线 | 免费看污在线观看 | 日韩久久精品一区二区三区下载 | av在线观| 美女视频国产 | 四虎成人精品在永久免费 | 国产婷婷色 | 欧美伦理一区二区三区 | 久久久久久影视 | 欧美无极色 | 在线激情影院一区 | 96av在线视频 | 丁香久久婷婷 | 夜夜操网 | 中文字幕av免费在线观看 | 欧美日韩视频一区二区三区 | 国产最顶级的黄色片在线免费观看 | 伊人久久精品久久亚洲一区 | av免费在线播放 | 四虎在线影视 | 丁五月婷婷 | 久久99精品久久久久蜜臀 | 激情欧美丁香 | 99视频精品免费视频 | 91中文在线视频 | 国产精品系列在线播放 | 午夜电影久久 | 能在线看的av | 成人av电影在线播放 | 一区在线电影 | 久九视频 | 97免费在线观看 | 精品91久久久久 | 亚洲人成人天堂h久久 | 亚洲欧洲精品视频 | 天堂av在线中文在线 | 色综合天天狠天天透天天伊人 | 国产精品久久久久久一区二区 | 免费av片在线 | 中国一级特黄毛片大片久久 | 日韩一二区在线观看 | 国产九九九九九 | 成年人三级网站 | 狠狠色免费 | av中文字幕网址 | 91香蕉视频在线下载 | 精品在线观 | 一区二区中文字幕在线观看 | 激情中文在线 | 91av中文字幕 | 亚洲黄色在线观看 | www.日日日.com | 99精品视频在线免费观看 | 久久天天躁 | 综合网色| 亚洲第一伊人 | 在线看黄色的网站 | 久久久午夜精品福利内容 | 911国产 | 99热这里只有精品在线观看 | 日本中文字幕观看 | 韩国一区视频 | 天天综合网天天综合色 | 天天鲁一鲁摸一摸爽一爽 | 亚洲精品视频在线观看视频 | 久草免费在线视频观看 | 免费日韩 精品中文字幕视频在线 | 亚洲精品久久激情国产片 | 色射爱| 在线免费看片 | 天天草网站 | 色婷婷亚洲婷婷 | 日韩在线免费不卡 | 天天射狠狠干 | 国产专区日韩专区 | 久久久久女人精品毛片九一 | 一区二区三区韩国免费中文网站 | 婷婷激情影院 | 天天射综合网站 | 骄小bbw搡bbbb揉bbbb | 久久久久久久久久久黄色 | 色婷婷综合久久久久中文字幕1 | 天天操婷婷 | 美女精品 | 欧美与欧洲交xxxx免费观看 | 久久国内精品99久久6app | 亚洲免费婷婷 | 成年美女黄网站色大片免费看 | 丁香影院在线 | 天堂久久电影网 | 久草视频免费观 | 国产精品人成电影在线观看 | 国产免费午夜 | 精选久久 | 在线观看精品一区 | 香蕉一区 | 免费精品国产va自在自线 | 91精品久久久久久综合乱菊 | 97人人人人 | 久久视屏网 | 制服丝袜欧美 | 欧美天堂视频在线 | 国际精品网 | 92av视频| 亚洲日本中文字幕在线观看 | 九色精品免费永久在线 | av免费在线观看网站 | 日本精品一二区 | 激情综合五月婷婷 | 草久久久| 色瓜 | 久久国产精品色av免费看 | 久久er99热精品一区二区 | 久久精品一区八戒影视 | 97超级碰碰碰视频在线观看 | 成人久久精品视频 | 亚洲观看黄色网 | 亚在线播放中文视频 | 99国产免费网址 | 五月在线| 国内少妇自拍视频一区 | 夜夜操天天 | 国产一区二区在线看 | 韩日三级在线 | 国产又粗又猛又黄 | 97电影手机| h动漫中文字幕 | 久草在线一免费新视频 | 91桃色在线播放 | 五月天六月丁香 | 国产午夜小视频 | 天天碰天天操 | 国产精品色在线 | 免费日韩在线 | 国产破处视频在线播放 | 日韩影视大全 | 亚洲午夜精品久久久久久久久久久久 | 欧美国产视频在线 | 免费观看完整版无人区 | 日韩av手机在线观看 | 国产不卡毛片 | 操操操夜夜操 | 日韩区在线观看 | 日韩av免费一区二区 | 国产亚洲视频中文字幕视频 | 国产一级黄色电影 | 国产韩国日本高清视频 | 国产精品福利无圣光在线一区 | 天天艹日日干 | 91九色精品 | 久久免费视频网站 | av官网在线 | 正在播放 久久 | 成人a毛片 | 91成人免费视频 | 成人免费在线视频 | 麻豆av电影 | 99精品在线观看视频 | 国产一区二区不卡视频 | 五月婷婷综合在线观看 | av大片免费 | 日韩av电影免费观看 | 一区二区精品在线 | 日韩在线观看一区二区 | 婷婷色伊人 | 91在线永久 | 亚洲精品中文在线资源 | 99久e精品热线免费 99国产精品久久久久久久久久 | 日韩偷拍精品 | 欧美日一级片 | 97av视频在线观看 | 奇米网网址 | 日韩一级片网址 | 成人免费在线电影 | 欧美一区二区三区不卡 | 久久精品久久99精品久久 | 国产区免费| 国内久久视频 | 精品视频免费 | 一级大片在线观看 | 在线免费观看一区二区三区 | 91污视频在线观看 | 蜜桃麻豆www久久囤产精品 | 91视频久久久 | 国内少妇自拍视频一区 | 人人玩人人添人人澡97 | 美国av大片| 国产黄色精品网站 | 国产日韩欧美在线 | 99久久精品免费一区 | 久久久午夜剧场 | 日韩欧美在线国产 | 久久国产精品久久精品 | 免费看精品久久片 | 最近更新中文字幕 | 美女国产免费 | 99热这里只有精品国产首页 | 天天射狠狠干 | 亚洲免费国产视频 | 日韩免费观看高清 | 三级在线视频观看 | 在线观看精品视频 | 美女免费黄视频网站 | 一区二区三区四区五区在线 | 日韩乱码在线 | 最新黄色av网址 | 亚洲性少妇性猛交wwww乱大交 | 亚洲精品在线二区 | 欧洲成人免费 | 日韩欧美视频一区二区 | jizzjizzjizz亚洲| 91香蕉亚洲精品 | 一区 二区电影免费在线观看 | 免费三级黄色 | 国产美女久久久 | 欧美 日韩 性 | 国产亚洲精品bv在线观看 | 国产视频一区二区在线观看 | 天天操偷偷干 | 久久久久久久久久久影院 | 国产综合在线视频 | 日韩精品一卡 | 五月天电影免费在线观看一区 | 超碰97在线资源 | 亚洲最新av网址 | 天天干天天摸 | 久草在线91 | 亚洲欧美日韩精品久久久 | 久久日韩精品 | 在线观看中文字幕第一页 | 手机看片国产日韩 | av一级久久| 又色又爽又激情的59视频 | 亚洲激情在线 | 伊人av综合 | 国产亚洲精品久久久久久久久久 | 夜夜夜夜夜夜操 | 色香蕉视频 | 久久综合久久综合久久综合 | 中文区中文字幕免费看 | 91福利视频网站 | 亚洲在线网址 | 91看片淫黄大片在线播放 | 久久99国产综合精品免费 | 黄色aaa级片 | 日本最新中文字幕 | 99夜色 | 激情综合婷婷 | 久久久久这里只有精品 | 一区二区精品在线 | 日韩激情综合 | 在线观看日本高清mv视频 | 国内精品久久久久久久久久久 | 99久久影院 | 操处女逼 | 欧美日韩一区二区三区视频 | 中文字幕乱码在线播放 | 超碰97国产精品人人cao | 欧美日韩视频在线观看免费 | 国产区免费 | 国产美女网站视频 | 国产成人精品一区二区三区在线观看 | 美国av大片 | 色婷婷久久久综合中文字幕 | 国产精品久久久久久久久久ktv | 亚洲精品在线观看的 | 91九色视频在线播放 | 久久这里只有精品视频首页 | 亚洲精品一区二区久 | 久久草网| 国内精品久久久久久 | 99久久精品免费看国产麻豆 | 中文字幕在线免费播放 | 免费午夜视频在线观看 | 日韩在线字幕 | 欧美日韩精品影院 | 精品国内自产拍在线观看视频 | 水蜜桃亚洲一二三四在线 | 天天人人| 国产精品中文字幕在线观看 | av大片免费 | 国产福利小视频在线 | 国产成人区 | 91视频久久久久 | 日韩欧美一区二区三区免费观看 | 亚洲精色| 国产精久久久久久久 | 日韩在线视频免费播放 | 免费a v在线 | 黄色一级大片在线免费看产 | 四虎5151久久欧美毛片 | 97视频在线 | 成全免费观看视频 | 五月天六月婷婷 | 国产精品99久久免费黑人 | 国产一级91 | 婷婷www | 成人在线免费看 | 日韩中文字幕免费视频 | 2018精品视频 | 婷婷在线免费观看 | 狠狠狠狠狠干 | 夜夜操天天摸 | www.黄色小说.com | 精品国产中文字幕 | 国产又粗又猛又黄又爽 | 日韩网站在线观看 | 999在线精品 | 日韩av高清 | 超碰人人乐 | 综合网五月天 | 精品久久久久久一区二区里番 | 中文字幕日韩在线播放 | 91精品欧美一区二区三区 | 中文字幕电影在线 | 亚洲欧美少妇 | 欧美成人xxxx| 欧美成人影音 | 亚洲一区二区精品视频 | 免费试看一区 | 91精品1区2区| 美女久久 | 午夜久久久久久久久久影院 | 丁香婷婷激情啪啪 | 日韩av手机在线看 | 日韩高清av | 婷婷视频在线 | 国产麻豆成人传媒免费观看 | 在线观看免费一级片 | 成人av视屏 | 一区二区三区四区五区在线 | 日日操天天操狠狠操 | 91视频免费看 | 手机av片 | 久久视频二区 | 在线精品一区二区 | 91av原创 | 九九欧美视频 | 久久最新 | 久久精品综合 | 欧美福利精品 | 国产一区二区不卡视频 | 日韩手机在线 | 丁香婷婷自拍 | 五月婷婷在线观看 | 免费在线观看av不卡 | 亚色视频在线观看 | 91香蕉久久| 成人免费视频免费观看 | 黄色美女免费网站 | 久久五月天综合 | 日韩国产在线观看 | 青青视频一区 | 黄网站色 | 天天操天天射天天插 | 日日天天狠狠 | 日韩在线观看a | 亚洲电影久久久 | 日本不卡123区 | 91麻豆免费版 | 亚洲综合激情 | 91网站免费观看 | 天天搞天天干 | 国产精品爽爽爽 | 国产人成一区二区三区影院 | 精品视频中文字幕 | 六月丁香激情综合 | 天天av在线播放 | 免费在线观看av网站 | 日韩美一区二区三区 | 国产高清成人在线 | 成人蜜桃网 | 免费日韩一级片 | 天天爽综合网 | 国产亚洲无 | 久久国产一区二区三区 | 丁香高清视频在线看看 | 国产在线中文 | 麻豆视频在线免费观看 | 日韩在线观看一区二区 | 日韩毛片在线播放 | 日韩一级成人av | 日韩精品欧美专区 | 欧美中文字幕久久 | 最近2019年日本中文免费字幕 | 久久精品婷婷 | 欧美视频在线二区 | 色婷婷视频在线 | 国产xxxx做受性欧美88 | 一本一道久久a久久综合蜜桃 | 亚洲视频专区在线 | 99精品国产一区二区三区麻豆 | 久久精品视频国产 | 精品久久久一区二区 | 手机在线看a| 女人18精品一区二区三区 | 国产黄色片在线免费观看 | 色久av| 日本久久久久久久久久久 | 国产精品久久久久久久久久 | 一区电影 | 中文字幕国产精品 | 国产精品久久久久一区 | 少妇bbbb搡bbbb搡bbbb | 黄色一级在线免费观看 | 丁香久久激情 | 福利一区在线视频 | 日韩视频三区 | 久久久影片 | 成人污视频在线观看 | 成人播放器 | 久久久久高清毛片一级 | 午夜精品久久 | 狠狠干综合网 | 91麻豆高清视频 | 91视频-88av | 69xxxx欧美 | 免费观看成人网 | 久久精品视频免费 | 成年人免费在线观看网站 | 91成年视频 | 亚洲国产字幕 | 黄色a在线| 激情久久伊人 | 亚洲综合导航 | 日韩99热 | 欧美另类xxx | 天天综合久久综合 | 日日操夜夜操狠狠操 | 国产一级片免费视频 | 欧美片一区二区三区 | 国产艹b视频| 69国产精品视频免费观看 | 亚洲精品无 | 激情久久一区二区三区 | 久久国产精品第一页 | 激情欧美一区二区三区 | 国产伦精品一区二区三区无广告 | 又黄又爽又刺激 | 美女视频黄免费的 | 精品国产区在线 | 久久免费视频一区 | 蜜桃视频在线观看一区 | 91精品啪啪| 91精品999 | 国产精品av免费观看 | 国产成人av网址 | 一级a性色生活片久久毛片波多野 | 黄色在线观看污 | 久久情侣偷拍 | 中文字幕婷婷 | 国产精品网在线观看 | 国产成人一区二区精品非洲 | 欧美一级视频免费 | 99精品在线 | 五月婷婷综合网 | 黄毛片在线观看 | 精品国产乱码一区二 | 国产精品男女 | 99热官网 | 国产精品对白一区二区三区 | 92中文资源在线 | 国产在线中文 | 国产精品久久久av久久久 | 六月丁香激情综合 | 亚洲1区 在线| 色偷偷97| 极品久久久久久久 | 国产精品国内免费一区二区三区 | 豆豆色资源网xfplay | 欧美a级在线免费观看 | www.国产在线 | 日本韩国精品在线 | 2021av在线 | 久热av| 国产精品乱看 | 欧美日韩国产精品一区二区亚洲 | avlulu久久精品| av品善网 | 福利一区二区在线 | 色先锋资源网 | 久久免费精品视频 | 亚洲三级黄色 | 久久久久二区 | 狠狠干,狠狠操 | 久久久久亚洲最大xxxx | 亚洲精品国产精品国 | 黄色影院在线免费观看 | 99国产在线视频 | 欧美日韩一二三四区 | 免费福利在线播放 | 99成人免费视频 | 免费观看一区二区 | 在线观看国产区 | 国产精在线 | 久久69av | 亚洲精品永久免费视频 | 高清不卡毛片 | 一本一道久久a久久精品 | 精品高清美女精品国产区 | 国产精品一码二码三码在线 | 久久久99精品免费观看app | 欧美日本一二三 | 日韩av免费大片 | 国产成人精品免高潮在线观看 | 欧美精彩视频 | 精品久久国产一区 | 久久久国产精品成人免费 | 久久久免费在线观看 | 国产精品免费一区二区三区在线观看 | 久久66热这里只有精品 | 国产精品尤物视频 | 手机av电影在线 | 免费日韩电影 | 国产精品免费观看网站 | 色亚洲网 | 日韩av片免费在线观看 | 九九免费观看视频 | 在线观看亚洲a | 日韩精品视频免费专区在线播放 | 国产免费a| 蜜桃视频在线观看一区 | 日韩精品在线免费播放 | 天天草天天色 | 久久电影色 | 国产精品久久亚洲 | 一区二区三区免费在线观看视频 | 麻豆传媒视频在线播放 | 中文字幕第一页在线视频 | 色视频网站在线观看一=区 a视频免费在线观看 | 国产精品一区二区果冻传媒 | 久久国产精品久久久久 | 亚洲人成人99网站 | 成人av高清在线 | 久精品视频在线观看 | 亚洲精品成人网 | 久久亚洲福利 | 久久99国产精品自在自在app | www.福利 | av在线永久免费观看 | a特级毛片 | 欧美亚洲另类在线视频 | 久久久96| 视频成人永久免费视频 | 久射网| 麻豆精品在线视频 | 久草在线最新免费 | 视频在线播放国产 | 亚洲狠狠丁香婷婷综合久久久 | 2021国产视频 | 亚洲午夜精品一区二区三区电影院 | 欧美福利精品 | 天天草天天操 | 国产视频一区在线免费观看 | 国产精品午夜av | 日韩av电影国产 | 中国一级特黄毛片大片久久 | 99视频精品 | 深爱激情五月综合 | 九九热在线免费观看 | 96在线| 亚洲成成品网站 | 国产在线播放不卡 | www日韩| 黄色在线观看污 | 国产免费视频一区二区裸体 | 欧美analxxxx| 中文在线资源 | 国产精品久久久久一区二区 | 国产成人福利在线 | 涩涩网站在线播放 | a久久免费视频 | 成年人看片 | 日韩在线观看第一页 | 国产91九色视频 | 91精品久久久久久 | 国产精品美乳一区二区免费 | 黄色小说视频在线 | 手机看片福利 | 黄色片网站免费 | 99 色| 久久99婷婷| 国产精品欧美久久久久久 | 日本公妇在线观看 | 亚洲欧美国产精品18p | 久久黄色a级片 | 日韩免费电影在线观看 | 免费看片日韩 | 亚洲综合导航 | 日韩在线视频在线观看 | 婷婷色影院 | 亚洲电影一区二区 | 亚洲欧美国产精品18p | 国产精品女同一区二区三区久久夜 | 黄色的网站在线 | 婷婷国产v亚洲v欧美久久 | 国产精品手机播放 | 中文字幕在线观看播放 | 特级a老妇做爰全过程 | 国产黄色成人 | 久久高清国产 | 97小视频 | 久久天天综合网 | 国产精品24小时在线观看 | 久久高清国产 | 成人免费在线播放视频 | 欧美日韩视频免费 | 久久噜噜少妇网站 | 日韩有色 | 久久精品视频在线播放 | 亚洲黄色片一级 | 色婷婷骚婷婷 | 一级全黄毛片 | 西西www4444大胆在线 | 欧美动漫一区二区三区 | 在线观看成年人 | 香蕉色综合 | 久久久精品小视频 | 伊人伊成久久人综合网站 | 欧美亚洲一级片 | 国产美女视频网站 | 亚洲成a人片77777kkkk1在线观看 | 91看片在线观看 | wwwwww黄|