国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

瀏覽器內核之 HTML 解釋器和 DOM 模型

Carbs / 1228人閱讀

摘要:書接上文瀏覽器內核之資源加載與網絡棧本文介紹的模型之后,深入的核心部分,剖析的解釋器是如何將從網絡或者本地文件獲取的字節流轉成內部表示的結構樹。事件處理最重要就是事件捕獲和事件冒泡這兩種機制。

微信公眾號:愛寫bugger的阿拉斯加
如有問題或建議,請后臺留言,我會盡力解決你的問題。
前言

此文章是我最近在看的【WebKit 技術內幕】一書的一些理解和做的筆記。
而【WebKit 技術內幕】是基于 WebKit 的 Chromium 項目的講解。

書接上文 瀏覽器內核之資源加載與網絡棧

本文介紹 W3C 的 DOM 模型之后,深入 WebKit 的核心部分,剖析 WebKit 的 HTML 解釋器是如何將從網絡或者本地文件獲取的字節流轉成內部表示的結構 --- DOM 樹。

1. DOM 模型 1.1.1 DOM 標準

DOM (Document Object Model)的全稱是文檔對象模型,它可以以一種獨立于平臺和語言的方式訪問和修改一個文檔的內容和結構。這里的文檔可以是 HTML 文檔、XML 文檔或者 XHTML 文檔。DOM 以面向對象的方式來描述文檔,在 HTML 文檔中,Web 開發者可以使用 JavaScript 語言來訪問、創建、刪除或者修改 DOM 結構,其主要目的是動態改變 HTML 文檔的結構。

使用 DOM 表示的文檔被描述成一個樹形結構,使用 DOM 的接口可以對 DOM 樹結構進行操作。

每一級的版本都對以前的版本進行了補充并伴隨新功能的加入,每個版本都對 DOM 的不同部分進行了定義。

1.1.2 DOM 樹 1.1.2.1 結構模型

DOM 結構構成的基本要素是 “節點” ,而文檔的 DOM 結構就是由層次化的節點組成。在 DOM 模型中,節點的概念很寬泛,整個文檔(Document )就是一個節點,稱為文檔節點。HTML 中的標記(Tag)也是一種節點,稱為元素(Element)節點。還有一些其他類型的節點,例如 屬性節點(標記的屬性)、Entity 節點、ProcessingIntruction 節點、CDataSection 節點、注釋(Comment)節點等。

1.1.2.2 DOM 樹

眾多的節點按照層次組織構成一個 DOM 樹結構。
如圖 5 - 4

DOM 樹的根就是 HTMLDocument , HTML 網頁中的標簽則被轉換成一個個的元素節點。同數據結構中的樹形結構一樣,這些節點之間也存在父子或兄弟關系。

1.2 HTML 解釋器 1.2.1 解釋過程

HTML 解釋器的工作就是將網絡或者本地磁盤獲取的 HTML 網頁和資源從字節流解釋成 DOM 樹結構。這一過程大致可以理解成圖 5-5所述的步驟。

這過程中,WebKit 內部對網頁內容在各個階段的結構表示。 WebKit 中這一過程如下:首先是字節流,經過解碼之后是字符流,然后通過詞法分析器會被解釋成詞語(Tokens),之后經過語法分析器構建成節點,最后這些節點被組建成一棵 DOM 樹。

1.2.2 詞法分析

在進行詞法分析之前,解釋器首先要做的事情就是檢查該網頁內容使用的編碼格式,以便后面使用合適的解碼器。如果解釋器在 HTML 網頁中找到了設置的編碼格式, WebKit 會使用相應的解碼器來將字節流轉換成特定格式的字符串。如果沒有特殊格式,詞法分析器 HTMLTokenizer 類可以直接進行詞法分析。

詞法分析的工作都是由 HTMLTokenizer 來完成 ,簡單來說,它就是一個狀態機---輸入的是字符串,輸出的是一個個詞語。因為字節流可能是分段的,所以輸入的字符串可能也是分段的,但是這對詞法分析器來說沒有什么特別之處,它會自己維護內部的狀態信息。

詞法分析器的主要接口是 “nextToken” 函數,調用者只需要關鍵字符串傳入,然后就會得到一個詞語,并對傳入的字符串設置相應的信息,表示當前處理完的位置,如此循環,如果詞法分析器遇到錯誤,則報告狀態錯誤碼,主要邏輯在圖 5-8 中給予了描述。

對于 “nextToken” 函數的調用者而言,它首先設置輸入需要解釋的字符串,然后循環調用 NextToken 函數,直到處理結束。 “nextToken” 方法每次輸出一個詞語,同時會標記輸入的字符串,表明哪些字符已經被處理過了。因此,每次詞法分析器都會根據上次設置的內部狀態和上次處理之后的字符串來生成一個新的詞語。 “nextToken” 函數內部使用了超過 70 種狀態,圖中只顯示了 3 種狀態。對于每個不同的狀態,都有相應的處理邏輯。

1.2.3 XSSAuditor 驗證詞語

當詞語生成之后,WebKit 需要使用 XSSAuditor 來驗證詞語流(Token Stream)。XSS 指的是 Cross Site Security , 主要是針對安全方面的考慮。

根據 XSS 的安全機制,對于解析出來的這些詞語,可能會阻礙某些內容的進一步執行,所以 XSSAuditor 類主要負責過濾這些被阻止的內容,只有通過的詞語才會作后面的處理。

1.2.4 詞語到節點

經過詞法分析器解釋之后的詞語隨之被 XSSAuditor 過濾并且在沒有被阻止之后,將被 WebKit 用來構建 DOM 節點。從詞語到構建節點的步驟是由 HTMLDocumentParser 類調用 HTMLTreeBuilder 類的 “constructTree” 函數來實現。

1.2.5 節點到 DOM 樹

從節點到構建 DOM 樹,包括為樹中的元素節點創建屬性節點等工作由 HTMLConstructionSite 類來完成。正如前面介紹的,該類包含一個 DOM 樹的根節點 ——HTMLDocument 對象,其他的元素節點都是它的后代。

因為 HTML 文檔的 Tag 標簽是有開始和結束標記的,所以構建這一過程可以使用棧結構來幫忙。HTMLConstructionSite 類中包含一個 “HTMLElementStack” 變量,它是一個保存元素節點的棧,其中的元素節點是當前有開始標記但是還沒有結束標記的元素節點。想象一下 HTML 文檔的特點,例如一個片段 “

”,當解釋到 img 元素的開始標記時,棧中的元素就是 body 、div 和 img ,當遇到 img 的結束標記時,img 退棧, img 是 div 元素的子女;當遇到 div 的結束標記時,div 退棧,表明 div 和它的子女都已處理完,以此類推。

同 DOM 標準一樣,一切的基礎都是 Node 類。在 WebKit 中, DOM 中的接口 Interface 對應于 C++ 的類,Node 類是其他類的基類,圖 5-10 顯示了 DOM 的主要相關節點類。圖中的 Node 類實際上繼承自 EventTarget 類,它表明 Node 類能夠接受事件,這個會在 DOM 事件處理中介紹。Node 類還繼承自另外一個基類 ——ScriptWrappable,這個跟 JavaScript 引擎相關。

Node 的子類就是 DOM 中定義的同名接口,元素類,文檔類和屬性類均繼承自一個抽象出來的 ContainerNode 類,表明它們能夠包含其他的節點對象。回到 HTML 文檔來說,元素和文檔對應的類注是 HTMLElement 類和 HTMLDocument 類,實際上 HTML 規范還包含眾多的 HTMLElement 子類,用于表示 HTML 語法中眾多的標簽。

1.2.6 網頁基礎設施

上面介紹了 Frame 、Document 等 WebKit 中的基礎類,這些都是網頁內部的概念,實際上,WebKit 提供了更高層次的設施,用于表示整個網頁的一些類,WebKit 中的 接口部分 就是基于它們來提供的,表示網頁的類既提供了構建 DOM 樹等操作,同時也提供了接口用于布局。渲染等操作。

1.2.7 線程化的解釋器

在 Renderer 進程中有一個線程,該線程用來處理 HTML 文檔的解釋任務,在 HTML 解釋器的步驟中,WebKit 的 Chromium 移植跟其他的 WebKit 移植也存在不同之處。

線程化的解釋器就是利用多帶帶的線程來解釋 HTML 文檔。因為在WebKit 中,網絡資源的字節流自 IO 線程傳遞給渲染線程之后,后面的解釋、布局和渲染等工作基本上就是工作在該線程,也就是渲染線程完成的(這不是絕對的)。因為 DOM 樹只能在渲染線程上創建和訪問,這也就是說構建 DOM 樹的過程只能在渲染線程中進行。但是,從字符到詞語這個階段可以交給多帶帶的線程來做,Chromium 瀏覽器使用的就是這個思想。

具體的實現過程:

字符串 (傳給)=> HTMLDocumentParser類 (創建一個新的對象)=> BackgroundHTMLParser 來負責處理 (交給)=> 前一步創建的對象

WebKit 會檢查是否需要創建用于解釋字符串的線程 HTMLParserThread 。如果該線程已存在,WebKit 就將剛剛的任務傳遞給這一新線程, 圖 5-13 描述了這一過程。

在 HTMLParserThread 線程中,WebKit 所做的事情包括將字符串解釋成一個個詞語,然后使用之前提到的 XSSAuditor 進行安全檢查。這是在一個新的線程中執行。主要區別在于解釋成詞語之后,WebKit 會分批次地將結果詞語傳遞給渲染線程。

1.2.8 JavaScript 的執行

在 HTML 解釋器的工作過程中,可能會有 JavaScript 代碼(全局作用域的代碼)需要執行,它發生在將字符串解釋成詞語之后、創建各種節點的時候。這也是全局執行的 JavaScript 代碼不能訪問 DOM 樹的原因——因為 DOM 樹還沒有被創建完。

所以建議 JavaScript 的使用如下:

1、將 “script” 元素加上 “async” 屬性,表明這是一個可以異步執行的 JavaScript 代碼。

2、將 “script” 元素放在 “body” 元素的最后,這樣它不會阻礙其他資源的并發下載。

但是不這樣做的時候,WebKit 使用預掃描和預加載機制來實現資源的并發下載而不被 JavaScript 的執行所阻礙。

具體做法是:當遇到需要執行 JavaScript 代碼的時候,WebKit 先暫停當前 JavaScript 代碼的執行,使用預先掃描器 HTMLPreloadScanner 類來掃描后面的詞語。如果 WebKit 發現它們需要使用其他資源,那么使用預資源加載器 HTMLPreloadScanner 類來發送請求,在這之后,才執行 JavaScript代碼。預先掃描器本身并不創建節點對象,也不會構建 DOM 樹,所以速度比較快。

當 DOM 樹構建完之后,WebKit 觸發 “DOMContentLoaded” 事件,注冊在該事件上的 JavaScript 函數會被調用。當所在資源都被加載完之后,WebKit 觸發 “onload” 事件。

WebKit 將 DOM 樹創建過程中需要執行的 JavaScript 代碼交由 HTMLScriptRunner 類來負責。工作方式很簡單,就是利用 JavaScript 引擎來執行 Node 節點中包含的代碼,具體可以參考 “HTMLScriptRunner::executeParsingBlockingScript” 方法。

1.3 DOM 事件機制 1.3.1 事件的工作過程

事件在工作過程中使用兩個主體,第一個是事件(event),第二個是事件目標(EventTarget)。WebKit 中用 EventTarget 類來表示 DOM 規范中 Events 部分定義的事件目標。

每個 事件都有屬性來標記該事件的事件目標。當事件到達事件目標(如一個元素節點)的時候,在這個目標上注冊的監聽者(Event Listeners)都會有觸發調用,而這些監聽者的調用順序不是固定的,所以不能依賴監聽者注冊的順序來決定你的代碼邏輯。

圖 5-17 是 EventTarget 接口的定義。圖中的接口是用來注冊和移除監聽者的。

事件處理最重要就是事件捕獲(Event capture)和事件冒泡(Event bubbling)這兩種機制。圖 5-18 是事件捕獲和事件冒泡的過程。

當渲染引擎接收到一個事件的時候,它會通過 HitTest(WebKit 中的一種檢查觸發gkwrd哪個區域的算法)檢查哪個元素是直接的事件目標。在圖 5-18 中,以 “img” 為例,假設它是事件的直接目標,這樣,事件會經過自頂向下和自底向上的兩個過程。

事件的捕獲是自頂向下,事件先是到 document 節點,然后一路到達目標節點。在圖 5-18 中,順序就是 “#document” -> "HTML" -> "body" -> "img" 這樣一個順序。事件可以在這一傳遞過程中被捕獲,只需要在注冊監聽者的時候設置相應參數即可。默認情況下,其他節點不捕獲這樣的事件。如果網頁注冊了這樣的監聽者,那么監聽者的回調函數會被調用,函數可以通過事件的 “stopPropagation” 函數來阻止事件向下傳遞。

事件的冒泡過程是從下向上的順序,它的默認行為是不冒泡,但是是事件包含一個是否冒泡的屬性。當這一屬性為真的時候,渲染引擎會將該事件首先傳遞給事件的目標節點的父親,然后是父親的父親,以此類推。同捕獲動作一樣,這此監聽函數也可以使用 “stopPropagation” 函數來阻止事件向上傳遞。

1.3.2 WebKit 的事件處理機制

DOM 的事件分為很多種,與用戶相關的只是其中的一種,稱為 UIEvent ,其他的包括 CustomEvent、MutationEvent 等。UIEvent 又可以分為很多種,包括但是不限于 FocusEvent、MouseEvent、KeyboardEvent、Composition 等。

基于 WebKit 的瀏覽器事件處理過程,首先是做 HitTest ,查找事件發生處的元素,檢查該元素有無監聽者。如果網頁的相關節點注冊了事件的監聽者,那么瀏覽器會把事件派發給 WebKit 內核來處理。同時,瀏覽器也可能需要理解和處理這樣的事件。這主要是因為,有些事件瀏覽器必須響應從而對網頁作默認處理。

EventHandler 類是處理事件的核心類,它除了需要將各種事件傳給 JavaScript 引擎以調用響應的監聽者之外,它還會識別鼠標事件,來觸發調用右鍵菜單、拖放效果等與事件密切相關的工作,而且 EventHandler 類還支持網頁的多框結構。EventHandler 類的接口比較容易理解,但是它的處理邏輯極其復雜。

圖 5-20 簡單描述了鼠標事件的調用過程,這一過程本身是比較簡單的,復雜之處在于 WebKit 的 EventHandler 類。

WebKit 中還有些跟事件處理相關的其他類,例如 EventPathWalker、EventDispatcher 類等,這些類都是為了解決事件在 DOM 樹中傳遞的問題。

1.4 影子(Shadow)DOM

影子 DOM 是一個新東西,主要解決了一個文檔中可能需要大量交互的多個 DOM 樹建立和維護各自的功能邊界的問題。

1.4.1 什么是影子 DOM

當開發這樣一個用戶界面的控件——這個控件可能由一些 HTML 的標簽元素組成,這些元素可以組成一顆 DOM 樹的子樹。這樣一個 HTML 控件可以被到處使用,但是問題隨之而來,那就是每個使用控件的地方都會知道這個子樹的結構。

當網頁的開發者需要訪問網頁 DOM 樹的時候,這些控件內部的 DOM 子樹都會暴露出來,這些暴露的節點不僅可能給 DOM 樹的遍歷帶來很多麻煩,而且也可能給 CSS 的樣式選擇帶來問題,因為選擇器無意中可能會改變這些內部節點的樣式,從而導致很奇怪的控件界面。

如何將內部的節點信息封裝起來,就像 C++ 語言的類一樣,同時又能夠將這些節點渲染出來呢 ? W3C 工作組提出的影子 DOM 概念。影子 DOM 的規范草案能夠使得一些 DOM 節點在特定范圍內可見,而在網頁的 DOM 樹中卻不可見,但是網頁渲染的結果中包含了這些節點,這就使得封裝變得容易很多。

圖 5-21 描述了 HTML 文檔對應的 DOM 樹和 “div” 元素包含的一個影子 DOM 子樹。當使用 JavaScript 代碼訪問 HTML 文檔的 DOM 樹的時候,通常的接口是不能直接訪問到影子 DOM 子樹中的節點的,JavaScript 代碼只能通過特殊的接口方式。

HTML5 支持了很多新的特性,例如對視頻、音頻的支持,讀者會發現這些元素其實是由很復雜的控制界面組成,這些界面也是使用 HTML 元素編寫,但是在 DOM 樹中,你無法找到相應的節點,這其實也是使用了影子 DOM 的思想。

因為影子 DOM 的子樹在整個網頁的 DOM 樹中不可見,那么事件是如何處理的呢 ?事件中需要包含事件目標,這個目標當然不能是不可見的 DOM 節點,所以事件目標其實就是包含影子 DOM 子樹的節點對象。事件捕獲的邏輯沒有發生變化,在影子 DOM 子樹內也會繼續傳遞。當影子 DOM 子樹中的事件向上冒泡的時候, WebKit 會同時向整個文檔的 DOM 上傳遞該事件,以避免一些很奇怪的行為。

1.4.2 WebKit 的支持

WebKit 已經支持影子 DOM 的規范草案,雖然還存在一些問題。支持影子 DOM 的相關類在目錄 “Source/core/dom/shadow” 下,里面的主要類是 ShadowRoot ,表示的是影子 DOM 的根節點。ShadowRoot 類繼承自 DocumentFragment 類,所以它同樣有 Node 節點的屬性和方法,因而在影子 DOM 樹的內部,遍歷樹沒有什么特別不同的地方。

當遍歷 HTML 文檔對應 DOM 樹的時候,WebKit 需要做特別的判斷,所以讀者會發現在 WebKit 的 Node 類實現中存在大量的條件語句,用來檢查當前節點是否是 ShadowRoot 對象,如果是該類的對象,把它作為不同 DOM 樹之間的邊界。有時候 WebKit 還需要對 ShadowRoot 對象作出特別處理,比如某些情況會略過它的子樹,同樣的,在事件處理的支持類 EventPathWalker 和 EventRetargeter 中,也需要做一些特別的處理邏輯,原理就是上面所述,細節不再介紹。

1.4.3 實踐:使用影子 DOM

示例代碼 5-2 給出了一個簡單的使用 webkitCreateShadowRoot 接口來創建影子 DOM 子樹的例子。網頁只包含了一個 “div” 元素,JavaScript 代碼使用該元素創建了一個影子 DOM 子樹的根節點,然后該根節點下加入了兩個子女,第一個是圖片元素,第二個是 “div” 元素,該元素內部包含了一些文本。

讀者可以打開 Chrom 瀏覽器的開發者工具,然后打開控制臺,在其中輸入 “document.firstChild.firstChild.nextElementSibling.firstElementChild.firstElementChild” 后會發現結果是空的,根據對應關系 “#document-> html -> head -> body -> div -> null”,雖然網頁中沒有 ‘head’ 元素,但是 DOM 樹仍然會創建該節點。同時讀者會發現 “div” 元素沒有子女,影子 DOM 子樹真的被隱藏起來了,成為真正的影子。

最后

希望本文對你有點幫助。

下期分享 第六章 CSS 解釋器和樣式布局 敬請期待。

對 全棧開發 有興趣的朋友可以掃下方二維碼關注我的公眾號 —— 愛寫bugger的阿拉斯加

分享 web 開發相關的技術文章,熱點資源,全棧程序員的成長之路。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/54597.html

相關文章

  • 《Webkit技術內幕》頁面渲染過程

    摘要:文章同步到技術內幕之頁面渲染過程最近拜讀了傳說中的技術內幕一書,有很大收獲,尤其是對頁面渲染有了較深的認識。解析語法分析,基于詞法解釋器生成的新標記,構建成抽象語法樹,解析器嘗試將其與某條語法規則進行匹配。 文章同步到github《Webkit技術內幕》之頁面渲染過程 最近拜讀了傳說中的《Webkit技術內幕》一書,有很大收獲,尤其是對頁面渲染有了較深的認識。由于功力有限,而且書中設...

    vvpvvp 評論0 收藏0
  • 《Webkit技術內幕》頁面渲染過程

    摘要:文章同步到技術內幕之頁面渲染過程最近拜讀了傳說中的技術內幕一書,有很大收獲,尤其是對頁面渲染有了較深的認識。解析語法分析,基于詞法解釋器生成的新標記,構建成抽象語法樹,解析器嘗試將其與某條語法規則進行匹配。 文章同步到github《Webkit技術內幕》之頁面渲染過程 最近拜讀了傳說中的《Webkit技術內幕》一書,有很大收獲,尤其是對頁面渲染有了較深的認識。由于功力有限,而且書中設...

    adam1q84 評論0 收藏0
  • 《Webkit技術內幕》頁面渲染過程

    摘要:文章同步到技術內幕之頁面渲染過程最近拜讀了傳說中的技術內幕一書,有很大收獲,尤其是對頁面渲染有了較深的認識。解析語法分析,基于詞法解釋器生成的新標記,構建成抽象語法樹,解析器嘗試將其與某條語法規則進行匹配。 文章同步到github《Webkit技術內幕》之頁面渲染過程 最近拜讀了傳說中的《Webkit技術內幕》一書,有很大收獲,尤其是對頁面渲染有了較深的認識。由于功力有限,而且書中設...

    forsigner 評論0 收藏0
  • 覽器內核 CSS 釋器樣式布局

    摘要:書接上文瀏覽器內核之解釋器和模型本文剖析的解釋器和樣式布局。根據生成解釋器類。而后將解釋后的信息設置到元素的屬性的樣式中,然后設置標記表明該元素需要重新計算樣式,并觸發重新計算布局。 showImg(https://segmentfault.com/img/remote/1460000016215814); 微信公眾號:愛寫bugger的阿拉斯加如有問題或建議,請后臺留言,我會盡力解決...

    _Dreams 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<