很多构造标签树的工具(如:W3C HTML lexicalanalyzer[W3C,1997]),它们各有特点,W3C HTML lexical analyzer有很强的通用性,适合各种标识语言;HTML Tidy 则能够自动发现并修正标签的错误。
3.2 DOM解析基本算法
3.2.1 基本算法思想
1. Html 中每个tag 都是作为树中的一个节点存在的,每个tag都属于树中的某一层。 2. 辅助数据结构:栈(stack)、List、HashTable。其中HashTable[i](i 属于int 类型)是一个List,用于临时存储第i 层子Tag。
3. 顺序扫描Html 文本,当遇到”
4. 遇到这样的标志,表示可能是某个Tag的结束。解析出此结束标志的Tag 名。如果在栈中找到与此结束标志名同名的元素(此元素属于栈中第iLevel 层),那么表示找到匹配的Tag。则Tag出栈,将HashTable[iLevel+1]到HashTable[maxLevel]中的所有元素取出作为此Tag 的子节点。放入第HashTable [iLevel]中。并记录Tag的结束位置。
5. 对于
6. 当栈为空的时候表示最后一次出栈的Tag 为根节点。 伪代码: public void Parse(){
char ch = GetCurrentChar(); //取第一个字符 while (!Eof()){
if (ch == '<'){
ch = MoveNext(); //取下一个字符
if ((ch >= 'A') && (ch <= 'Z') || (ch == '!')) { iBeginPos = Index; //记录开始位置 //表示可能是一个标签
HtmlTag tag = GetTag(); //解析此Tag if (tag != null) { //首先判断是否有文本
if (m_CurrentText.Lenght > 0) { //将文本作为一个普通Tag入栈
Stack.Push(new HtmlTextTag(m_CurrentText)); }
tag.BeginPos = iBeginPos; //记录此Tag的开始位置 Stack.Push(tag); //把Tag入栈 } }
ch = GetCurrentChar(); if (ch == '/'){
//可能是结束标签 tagName = GetTagName();
//从上到下查看Stack,如果Tag中存在 if (FindInStack(tagName)){
//在栈中找到名为tagName的元素,则把找到的元素出栈
PopTag(tagName); } } else{
//xxx之间的文本xxx,这里将作为TextTag来处理 m_CurrentText.Append(GetCurrentChar()); }
//继续处理下一个字符 ch = MoveNext(); }
//解析完成以后,如果栈不空,那么把元素出栈,并把最后一次出栈的元素作为根 if (Stack.Count > 0){
HtmlTag tag = null; while (Stack.Count > 0){
tag = Stack.Pop(); PopTag(tag); }
//最后一个元素作为根元素 if (tag != null){
m_listRoot.Add(tag); } } }
private void PopTag(HtmlTag tag) {
int iLevel = Stack.Count;
//找到了元素,把iLevel到m_IMaxLevel中所有的元素按照全部作为tag的子元素 for (int i = iLevel + 1; i < m_iMaxLevel; i++){
for (j = 0; j < HashTable[i].Count; j++){
tag.Children.Add(HashTable[i][j]); } }
//表示栈已经为空,那么最后一次出栈的tag将作为根 if (Stack.Count == 0){
m_listRoot.Add(tag); } }
private void PopTag(string tagName){ /**
* 元素出栈的时候,首先需要把当前已经存在了的HtmlTextTag入栈
* 比如:文本段1文本段2文本段3182. * 在Parse中,当解析出入栈前,需要先把\文本段1\入栈 * 在这里,解析出了结束标志 * 那么首先需要把\文本段2\入栈。 * 解析出则需要把\文本段3'入栈。
* 这样才能够保证\文本段1\和\文本段3\成为的子节点, * 而\文本段2\作为的子节点 */
if (m_CurrentText.Lenght > 0){
//将文本作为一个普通Tag入栈
Stack.Push(new HtmlTextTag(m_CurrentText)); }
HtmlTag tag = Stack.Pop(); //元素出栈 int iLevel = Stack.Count; //记录栈元素数 while (tag.Name != tagName) {
//将tag放入第iLevel层的List中 HashTable[iLevel].Add(tag); tag = Stack.Pop(); iLevel = Stack.Count; }
//元素出栈后续处理 PopTag(tag); }
private HtmlTag GetTag(){
//则表示是注释
if (\如果发现是< !--开头的元素\
SkipComment(); }
HtmlTag tag = new HtmlTag();
tag.Name = GetTagName();
//这里的Attribute我将其作为HashTable类型,Hash[属性名]=属性值 tag.Attribute = GetTagAttribute(); return tag; }
4 WebKit的Ports介绍
4.1 Port概述
WebKit Port 方面的内容很广,可将不同的图形库、网络库与WebCore集成,提供不同的Port接口供外部程序使用等,例如同样在windows 平台上可以运行的Google Chrome 和Safari 就是针对WebKit在不同平台的移植。
Port 方面的主要内容是提供不同的Port 接口供外部程序使用以及如何与外部程序交
互。WebKit 中的其它两部分WebCore、 Javascript 实现,从逻辑上讲是不直接提供接口给外部程序使用的,同时WebKit为了完成浏览器的核心功能,也需要从外部程序中通过Port 接口的方式获取一些支持。
WebKit 作为一个相对独立的整体,它与外部程序之间的交互也就有一组相对固定的接
口来定义及维护它们之间的关系,它们之间的关系与插件跟浏览器引擎之间的关系完全类似,接口相当一组协议,有的是由WebKit 来实现,而供外部程序调用,有的的正好相反。
通过前面的了解我们知道WebKit的主要功能集中在分析Html、渲染布局Web 内容以及
Javascript 实现方面等,而这些Web 内容显示在哪个窗口及消息处理的启动循环等都需要由外部程序来提供,这一切都由Ports来完成。
4.2 WebKit Port移植实现分析
4.2.1 WebCore交互接口
在 WebKit源代码目录结构中WebKit目录下分别包含gtk、mac、qt、win、wx 目录,其