WebKit 内核源码分析 (五)
本文分析WebKit中html的解析過程,DOM節點樹的建立。
關鍵詞:WebKit,html解析,html tree construction,WebCore,
DOM節點樹,dlmu2001
1.????HTML解析模型
???????????????????????????????????????????????????????????????????????????????圖1?HTML解析模型圖
上圖是HTML解析模型圖,HTML解析分成Tokeniser和Tree Construction兩個步驟,在”WebKit中的html詞法分析”
(http://blog.csdn.net/dlmu2001/archive/2010/11/09/5998130.aspx)一文中,我們已經對Tokeniser這一步進行了分析,本文的目標是Tree Construction這一步。
Tree Construction輸入是token流,輸出是DOM節點樹。
2.????DOM樹
HTML DOM定義了一套標準來將html文檔結構化,它定義了表示和修改文檔所需的對象、這些對象的行為和屬性以及對象之間的關系,可以把它理解為頁面上數據和結構的一個樹形表示。
Node是DOM模型中的基礎類,它可以分成13類(見NodeType),在HTML解析中,最常見的是Document,Element,Text三類。
l??Document是文檔樹的根節點,在HTML文檔中,他派生為HTMLDocument。
l??在文檔中,所有的標簽轉化為Element類,一般它有標簽名,并根據標簽名繼承為特定的子類。
l??Element之間的原始文本轉化成Text類。
以一個簡單的html頁面為例:
<html>
<head>
<title>test</title>
</head>
<body>
<h1>hl1</h1>
<h2>hl2</h2>
<h3>hl3</h3>
</body>
</html>
經過解析后的節點樹如下(忽略換行符):
圖2 HTML DOM節點樹示例
如果沒有忽略換行符,則每個換行符就是一個Value為”\n”的Text節點。
3.????Tree Construction原理
將圖二中的節點樹以WebKit中的類具體化(同樣忽略換行符)。
圖3 Webkit HTML DOM節點樹示例
看到這里,你是不是覺得仿佛看到了一個呼之欲出的Tree Construction輪廓?是的,最簡化的情況就是這樣,根據輸入的token,創建出相應的Element派生類,然后添加到DOM樹中合適的位置,這就是Tree Construction干的事情。當然,添加到合適的位置,這個需要一系列復雜的規則,另外,WebKit將Render樹的創建也放到了Tree Construction階段中來,再加上CSS,Javascript,所以,這就是你看到的復雜的代碼。
放出兩個函數原型,熱熱身,培養培養感情。
[cpp]?view plaincopyTree Construction流程由一個狀態“Insertion Mode”進行控制,它影響token的處理以及是否支持CDATA部分,HTML5中給出了詳細的規則(http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-insertion-mode)。它也控制了在特定狀態下能夠處理的token,比如在head里面,再出現head標簽,顯然是不應該處理的。
4.????開放元素堆棧
為了維護即將解析的標簽同已解析的標簽之間的關系(此時即將解析的標簽還沒有加入到DOM樹中),引入了開放元素堆棧m_openElements,初始狀態下,這個堆棧是空的,它是向下增長的,所以最上面的節點是最早加入到堆棧中的,在html文檔中,最上面的節點就是html元素,最底部的節點就是最新加入到堆棧中的。Tree Builder的時候,每碰到一個StartTag的token,就會往m_opnElements中壓棧,碰到EndTag的token,則出棧。像Character這樣的token,則不需要進行壓棧出棧的動作,只有可以包含子節點的tag,才做壓棧出棧的動作。Html5的文檔中對開放元素堆棧也有說明,http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements。
對于正在解析的token,除了根節點html,它必然是堆棧底部元素(m_openElements.top())的子節點,所以在形成DOM樹的時候,就可以通過ContainerNode::parserAddChild這樣的接口加入到DOM節點樹中。
除了正常的堆棧和壓棧,對于html,head,body元素,棧結構(HTMLElementStack)中有專門的成員m_htmlElement,m_headElement,m_bodyElement記錄,主要是用于檢錯糾錯處理。
在本文的html范例中,當解析到<h2>hl2</h2>的hl2這個character的token的時候,它的開放元素堆棧如下,HTMLHeadingElement是堆棧的top,所以它是hl2這個Text節點的parent。
圖4?開放元素堆棧示例
此時的DOM節點樹如下:
圖5 Webkit DOM節點數示例
5.????元素的創建
HTMLElementFactory類提供了元素的創建方法createHTMLElement。傳入為對應的標簽名,所屬的document,所屬的form(如果屬于form),在parser的時候,最后一個參數為true。
[cpp]?view plaincopy在HTMLElementFactory中,通過一個Hash Map將tag name和對應的元素構造函數對應起來(gFunctionMap)。tag一般對應一個派生于HTMLElement的類。如下是HTMLHeadingElement的類層次結構圖。
圖6 HTMLHeadingElement類層次圖
6.????其它
HTMLConstructionSite::attach中的attach一詞,地瓜理解主要是attach到DOM節點數上,當然,它同時調用了Element::attach,Element類的attach主要是attach到Render樹上,它會創建對應該Element的RendrObject。
除了m_openElements,HTMLConstructionSite同時維護了Format?元素列表m_activeFormattingElements,Formating元素就是那些格式化標簽,包括a,b,big,code,em,font,I,fot,I,nobr,s,small,strike,strong,tt,u。為了處理這些Formatting元素的嵌套關系(此時它們可能不是父子關系,而是平級,不加入到m_openElements),HTML5引入了這個列表(http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#list-of-active-formatting-elements)。
使用gdb調試的童子,可以運行Tools/gdb/webkit.py腳本,在print結構體的時候得到易于理解的表示,還可以打印出節點樹,具體參考http://trac.webkit.org/wiki/GDB。
總結
以上是生活随笔為你收集整理的WebKit 内核源码分析 (五)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WebKit 内核源码分析 (四)
- 下一篇: WebKit中的Chrome和Chrom