HTML解釋器和DOM模型

DOM模型

DOM樹

DOM結構構成的基本要素爲”節點”,而文檔的DOM結構就是有層次化的節點組成,在DOM模型中,節點的概念很寬泛,整個文檔(Document)就是一個節點,稱爲文檔節點,HTML中的標記(Tag)也是一種節點,成爲元素節點,還有一些其他類型的節點,例如屬性節點,Entity節點,CDataSection節點,註釋(Comment)節點等。由於DOM的定義是與語言無關的,所以標準中所有這些都是接口,同時,因爲支持不同類型的語言,所以沒有對內存的管理機制做任何方面的規定,同時這些不同語言的不同實現只需要符合標準定義的接口即可,而實現者通常可以把這些實現細節隱藏起來。

DOM中Document的IDL接口定義如下:

interface Document : Node {
readonly attribute DocumentType doctype;
readonly attribute DOMImplementation implementation;
readonly attribute Element documentElement;
Element createElement(in DOMString tagName) raises(DOMException);
DocumentFragment createDocumentFragment();
Text createTextNode(in DOMString data);
Comment createComment(in DOMString data);
CDATASection createCDataSection(in DOMString data) raises(DOMException);
ProcessingInstruction createProcessingInstruction(in DOMString target, in DOMString data) raises(DOMException);
Attr createAttribute(in DOMString name) raises(DOMException);
EntityReference createEntityReference(in DOMString name) raises (DOMException);
NodeList getElementsByTagName(in DOMString tagname); 
};

其中重點關注的HTML文檔繼承自文檔接口,同時又有些自己的擴展,包括新的屬性和接口,這些都跟HTML文檔的具體應用有關,具體接口如下:

interface HTMLDocument : Document {
attribute DOMString title;
readonly attribute DOMString referrer;
readonly attribute DOMString domain;
readonly attribute DOMString URL;
attribute HTMLElement body;
readonly attribute HTMLCollection images; 
readonly attribute HTMLCollection applets; 
readonly attribute HTMLCollection links; 
readonly attribute HTMLCollection forms; 
readonly attribute HTMLCollection anchors;
attribute DOMString cookie;
void open();
void close();
void write(in DOMString text);
void writeln(in DOMString text);
Element getElementById(in DOMString elementId);
NodeList getElementByName(in DOMString elementName);
};
DOM樹

將DOM節點和各種子節點按照層次結構組織構成一個DOM樹行結構,舉例如下:
這裏寫圖片描述
DOM樹的根就是HTMLDocument,HTML網頁中的標籤則被轉換成一個個的元素節點,同數據結構中樹形結構類似,這些節點之間也存在着對應的父子和兄弟關係。

HTML解釋器

解釋過程

解釋器的作用就是將本地磁盤或者網絡獲取的HTML網頁和資源從字節流解釋成DOM樹結構,Webkit的處理過程如下:
這裏寫圖片描述
WebKit爲了完成這個過程,引入了比較複雜的基礎設施類,下圖描述了WebKit在構建DOM樹時需要使用到的主要類:
這裏寫圖片描述
框對應與“Frame”類,而文檔對應於“HTMLDocument”類,所以框內包含文檔,HTMLDocument類繼承自Document類,爲了遵循DOM標準,因爲Document有兩個子類,另一個爲XMLDocument,右邊FrameLoader類是框中內容的加載器,類似與資源和資源的加載器,因爲Frame對象中包含Document對象,所以WebKit需要DocumentLoader類幫助加載HTML文檔並從字節流到構建成DOM樹,DocumentWriter類是一個輔助類,它會創建DOM樹的根節點HTMLDocument對象,該類包括兩個成員變量,一個用於文檔的字符解碼,另一個爲HTML解釋器HTMLDocumentParser類。其中HTMLDocumentParser是一個管理類,包括了用於各種工作的其它類,例如字符串到詞語需要用到詞法分析器HTMLTokenizer類,該管理類讀入字符串,輸出一個詞語,這些詞語經過XSSAuditor做完安全檢查之後,輸出到HTMLTreeBuilder類,HTMLTreeBuilder類負責DOM樹的建立,它本身能夠通過詞語創建一個個的節點對象,然後藉由HTMLConstructionSite類來將這些節點對象構建成一個DOM樹。

詞法分析到構建節點

在進行詞法分析之前解釋器要檢查網頁內容使用的編碼格式,以便後面使用合適的解碼器,webkit使用相應的解碼器將字節流轉換成字符串。詞法分析的工作由HTMLTokenizer類來完成,輸入的爲字符串,輸出的爲一個個的詞語,詞法分析器的主要接口爲“nextToken”函數,對於該函數的調用者而言,它首先設置需要解釋的字符串,然後循環調用NextToken函數知道處理結束,“nextToken”方法每次輸出一個詞語,同時會標記輸入的字符串,表明哪些字符串已經被處理過了,因此每次詞法分析器都會根據上次設置的內部狀態和上次處理之後的字符串來生成一個新的詞語,“nextToken”函數內部使用了超過70種狀態,對於每個不同的狀態都會有相應的處理邏輯,對於詞語的類別,HTMLToken類定義了6種詞語類別,包括DOCTYPE, StartTag,EndTag,Comment,Character和EndOfFile。根據XSS的安全機制,對於解析出來的詞語可能會阻礙某些內容的進一步處理,因此XSSAuditor類會過濾這些被阻止的內容,通過的詞語將被WebKit用來構建DOM節點,這一步奏由HTMLDocumentParser類調用HTMLTreeBuilder類的“constructTree”函數來實現,該函數實際利用processToken“函數來處理HTMLToken定義的6中詞語類型。
詞法分析器HTMLTokenizer的主要工作流程:
這裏寫圖片描述

節點到DOM樹

從節點到構建DOM樹,包括爲樹中的元素節點創建屬性節點等工作由HTMLConstructionSite類來完成,DOM樹的根節點爲HTMLDocument,其他元素的節點都是它的後代,由於HTML文檔中的Tag標籤是有開始和結束標記的,因此可以使用棧,HTMLConstructionSite類中包含一個“HTMLElementStack”變量,它是一個保存元素節點的棧,其中的元素節點是當前有開始標記當時還沒有結束標記的元素節點,如下的一個片段”< body >< div >< img >< /img >< /div >< /body >”,當解釋到img元素的開始標記時,棧中的元素是body,div,img當遇到img的結束標記時,img退棧,img是div元素的子女,當遇到div的結束標記時,div退棧,表明div和它的子女以處理完畢,以此類推。DOM結構的基本爲Node類,在WebKit中,DOM中的接口Interface對應與C++類,Node類是其他類的基類,下圖顯示了DOM的主要相關節點類:
這裏寫圖片描述
圖中的Node類繼承自EventTarget類,表明Node類能夠接收事件,此外還繼承自ScriptWrappable類,這個與JavaScript引擎有關,Node的子類是DOM中定義的同名接口,元素類,文檔類和屬性類均繼承自一個抽象出來的ContainerNode類,表明它們能偶包含其他的節點對象。

DOM事件機制

事件的工作過程

事件在工作過程中使用兩個主體,第一個是事件,第二個是事件目標,每個事件都有屬性來標記該事件的事件目標,當事件到達事件目標的時候,在這個目標上註冊的監聽者都會被觸發調用,其中監聽者的調用順序是不固定的,EventTarget的接口定義:

interface EventTarget {
  void addEventListener(in DOMString type, in EventListener listener, in boolean userCapture);
  void removeEventListener(in DOMString type, in EventListener listener, in boolean userCapture);
  Boolean dispatchEvent(in Event evt) raises (EventException);
};

事件處理主要有事件捕獲和事件冒泡兩種機制,過程如下:
這裏寫圖片描述
當渲染引擎接收到一個事件的時候,會通過HitTest檢查哪個元素是直接的事件目標,以上圖“img”爲例,假設它是事件的直接目標,這樣事件會經過自頂向下和自底向上兩個過程,事件的捕獲是向下的,事件先到document節點,然後一路到達目標節點,事件可以在這一傳遞過程中被捕獲,只需要在註冊監聽的時候設置相應的參數即可,EventTarget類中的“addEventListener”的第三個參數就是表示這個含義,默認情況下,其他節點不捕獲這樣的事件,如果網頁註冊了這樣的監聽者,那麼監聽者的回調函數會被調用,函數可以通過事件的“stopPropagation”函數來阻止事件向下傳遞。事件的冒泡過程是從下向上的順序,它的默認行爲是不冒泡,但是事件包含一個是否冒泡的屬性,當這一屬性爲真的時候,渲染引擎會將該事件首先傳遞給事件目標節點的父親,以此類推,同捕獲動作一樣,這些監聽函數也可以使用“stopPropagation”函數來阻止事件向上傳遞。

WebKit事件處理機制

DOM事件分爲很多種,與用戶相關的只是其中的一種UIEvent,其它的包括CustomEvent,MutationEvent等,UIEvent又可以分爲很多種,包括但是不限於FocusEvent,MouseEvent,KeyboardEvent,CompositionEvent等。基於WebKit的遊覽器事件處理過程,首先是做HitTest,查找事件發生出的元素,檢測該元素有無監聽者,如果網頁的相關節點註冊了事件的監聽者,那麼遊覽器會把事件派發給WebKit內核來處理,同時,遊覽器也可能需要理解和處理這樣的事件,因爲有些事件遊覽器必須響應從而對網頁作默認處理,若想禁止遊覽器做默認處理則可以調用事件的”preventDefault“函數。

影子(Shadow)DOM

影子DOM主要是解決了一個文檔中可能需要大量交互的多個DOM樹建立和維護各自的功能邊界的問題,影子DOM能夠使得一些DOM節點在特定範圍內可見,而在網頁的DOM樹中卻不可見,但是網頁渲染結果中包含了這些節點。下圖描述了HTML文檔對應的DOM樹和”div”元素包含的一個影子DOM子樹:
這裏寫圖片描述
當使用JavaScript代碼訪問HTML文檔的DOM樹的時候,通常的接口是不能直接訪問到影子DOM樹中的節點的,JavaScript只能通過特殊的接口方式。HTML5對視頻、音頻提供了支持,這些元素有很複雜的控制界面組成,這些界面也是使用HTML元素編寫,但是在DOM樹中,無法找到相應的節點,這就是採用了影子DOM的思想,影子DOM中,所有事件目標都是包含影子DOM子樹的節點對象,事件捕獲的邏輯沒有發生變化,在影子DOM子樹內也會繼續傳遞,當影子DOM子樹中的事件向上冒泡的時候,WebKit會同時向整個文檔的DOM上傳遞該事件。

發佈了39 篇原創文章 · 獲贊 11 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章