加載部分HTML文本(即主資源)后便可以開始解析HTML元素(對輸入字節流進行逐字掃描,識別HTML元素),最后生成DOM樹,本文只講HTML解析。
HTML解析部分時序圖:

其中最為重要的過程是(1)startToken(2)nextToken(3)endToken(4)constructTreeFromHTMLToken,這里的4步是循環執行的,當輸入字符結束時,則跳出循環。
HTMLTokenizer::nextToken則創建了token,然后可以根據token創建html元素,解析的整個過程就是一個狀態機
HTML解析狀態機:

初始狀態為DataState,狀態機結束后會返回一個HTMLToken對象,不同的結束方式(上圖中有三個出口1、2、3)HTMLToken的類型也會不同。
class HTMLToken { enum Type { Uninitialized, DOCTYPE, StartTag, EndTag, Comment, Character, EndOfFile, }; } ;
對於1結束位置:Type=EndOfFile
對於2結束位置:Type=Character
對於3結束位置:解析注釋-Type=Comment,解析文檔類型-Type=DOCTYPE,解析標簽(Type=StartTag/EndTag)
子狀態機/解析注釋:

子狀態機/解析文檔類型:

子狀態機/解析標簽名、屬性名、屬性值


最后以一個簡單的HTML為例,描述解析過程:
<!Doctype html>
<!--comment-->
<html>
<body>
<p>First name:</p>
<input type="text"/>
</body>
</html>
1.解析文檔類型
'<!Doctype html>',DataState狀態遷移到TagOpenState狀態
'<!Doctype html>',TagOpenState狀態遷移到MarkupDeclarationOpenState狀態
'<!Doctype html>',MarkupDeclarationOpenState狀態遷移到DOCTYPEState狀態
'<!Doctype html>',DOCTYPEState狀態遷移到BeforeDOCTYPENameState狀態
'<!Doctype html>',BeforeDOCTYPENameState狀態遷移到DOCTYPENameState狀態,並執行beginDOCTYPE
'<!Doctype html>',DOCTYPENameState狀態遷移到DOCTYPENameState狀態,並執行appendToName
'<!Doctype html>',結束
2、解析注釋
'<!--comment-->',DataState狀態遷移到TagOpenState狀態
'<!--comment-->',TagOpenState狀態遷移到MarkupDeclarationOpenState狀態
'<!--comment-->',MarkupDeclarationOpenState狀態遷移到CommentStartState狀態
'<!--comment-->',CommentStartState狀態遷移到CommentState狀態,並執行appendToComment
'<!--comment-->',CommentState狀態遷移到CommentState狀態,並執行appendToComment
'<!--comment-->',CommentState狀態遷移到CommentEndDashState狀態
'<!--comment-->',CommentEndDashState狀態遷移到CommentEndState狀態
'<!--comment-->',結束
3、解釋'html'元素
'<html>',DataState狀態遷移到TagOpenState狀態
'<html>',TagOpenState狀態遷移到TagNameState狀態,並執行beginStartTag
'<html>',TagNameState狀態遷移到TagNameState狀態,並執行appendToName
'<html>',結束
4、解釋'body'和'p'元素,同3
6、解析'p'元素內容
'First name:</p>',DataState狀態遷移到DataState狀態,並執行bufferCharacter
'First name:</p>',DataState狀態遷移到DataState狀態,並執行bufferCharacter
'First name:</p>',判斷bufferCharacter是否存在字符,存在則結束
7、解析'/p'元素
'</p>',DataState狀態遷移到TagOpenState狀態
'</p>',TagOpenState狀態遷移到EndTagOpenState狀態
'</p>',EndTagOpenState狀態遷移到TagNameState狀態,並執行beginEndTag
'</p>',結束
8、解析'input'元素
'<input type="text" />',DataState -> TagOpenState
'<input type="text" />',TagOpenState -> TagNameState,並執行beginStartTag
'<input type="text" />',TagNameState -> TagNameState,並執行appendToName
'<input type="text" />',TagNameState -> BeforeAttributeNameState
'<input type="text" />',BeforeAttributeNameState -> AttributeNameState,並執行beginAttribute
'<input type="text" />',AttributeNameState -> AttributeNameState,並執行appendToAttributeName
'<input type="text" />',AttributeNameState -> BeforeAttributeValueState
'<input type="text" />',BeforeAttributeValueState -> AttributeValueDoubleQuotedState
'<input type="text" />',BeforeAttributeValueState -> AttributeValueDoubleQuotedState,並執行appendToAttributeValue
'<input type="text" />',AttributeValueDoubleQuotedState -> AttributeValueDoubleQuotedState,並執行appendToAttributeValue
'<input type="text" />',AttributeValueDoubleQuotedState -> AfterAttributeValueQuotedState,並執行endAttribute
'<input type="text" />',AfterAttributeValueQuotedState-> BeforeAttributeNameState
'<input type="text" />',BeforeAttributeNameState-> SelfClosingStartTagState
'<input type="text" />',結束,並執行setSelfClosing
9、解析'/body'和'/html'元素,同7
參考文章:
