Mozilla FireFox Gecko内核源代码解析
(4.nsHTMLTokens)
中科院计算技术研究所网络数据科学与工程研究中心
信息抽取小组
耿耘
之前我们分析了nsHTMLTokenizer(详见其解析篇),其中我们了解到了,其中设计了如何配合 nsScanner对输入流循环地解析流程,如怎么进行回溯等流式操作。实际上其中并没有包含具体的字符比对,以及正则表达式匹配等工作。同时也没有具体地声明“哪些HTML标签才是合法的标签”等具体信息。
而这些信息实际上都包含在了nsHTMLTokens这个文件中。在这个文件中除了对HTML中常见的各种Tag和他们最后生成的Token进行了一一的对应,并且对Tokens的类型,名称,状态,结构等进行了定义。并且对于一些类型的Token,都提供了相应的Consume方法,该种方法就是提供给在nsHTMLTokenizer中所调用的,真正对相应的Token所对应的HTML文本进行解析的方法。
通过学习这个nsHTMLTokens,还可以对Mozilla所推出的HTML标准进行了解,可以知道它都包含什么样的Tokens,哪些Tokens是合法的,以及他们的属性都是如何在浏览器中存储的。
首先,我们来看它的头文件nsHTMLTokens.h,其中包含了哪些声明和定义,这个头文件比较长,因此我们对其的内容逐个进行解析
首先来看他在文件头写下的一段注释:
这个文件中包含了我们的HTML Token的类型定义,这个定义能够被我们的DTD所理解。实际上,这一套Token定义也可以用于XML解析。目前我们拥有文本,注释,起始型标签,结束型标签,实体,属性,样式,脚本以及省略内容,这些个类型的Tokens。空格和换行符同样拥有他们所对应的Token,但是它们在未来的版本中可能会被消除掉。
如果你想查看HTML的标签定义,请查看叫做nsHTMLTags.h/cpp的文件。
大部分的Token类型都有相似的API。他们都有获取Token类型的方法(GetTokenType);那些代表HTML Tag的Tokens还有一个获取tag类型的方法(GetTypeID)。另外,大部分的Token都有一个在解析流程中帮助它们自己进行解析的调用方法(Consume)。我们同样还提供了一些调试用的代码。
下面我们来看代码:
#ifndefHTMLTOKENS_H
#defineHTMLTOKENS_H
#include "nsToken.h"
#include "nsHTMLTags.h"
#include "nsString.h"
#include "nsScannerString.h"
classnsScanner;
/*******************************************************************
* This enum defines the set of token typesthat we currently support.
*******************************************************************/
//下面这个枚举类型,定义了我们目前所支持的Token类型,这个很重要,相当于HTML的规范定义其中的一部分,我们可以看到Mozilla一共将所有的Token分为了如下的14种类型,其中的两种类型unknown和last是浏览器内部使用的,不属于HTML规范
enumeHTMLTokenTypes {
eToken_unknown=0,
eToken_start=1, eToken_end, eToken_comment, eToken_entity,
eToken_whitespace, eToken_newline, eToken_text, eToken_attribute,
eToken_instruction, eToken_cdatasection, eToken_doctypeDecl, eToken_markupDecl,
eToken_last //make sure this stays the lasttoken...
};
//这个从字面上就能看出,
nsresult ConsumeQuotedString(PRUnicharaChar,nsString& aString,nsScanner& aScanner);
//这个从字面上就能看出是对属性文本进行解析的
nsresult ConsumeAttributeText(PRUnicharaChar,nsString& aString,nsScanner& aScanner);
这个是通过一个整形的标示位来获取TagName的
constPRUnichar* GetTagName(PRInt32 aTag);
//PRInt32 FindEntityIndex(nsString&aString,PRInt32 aCount=-1);
下面我们来看一个HTMLToken的基类,他是用于为其他类进行继承而服务的,其中定义了一些基本的属性和方法,后面的几个Token类都是基于这个类建立的。
/**
* Thisdeclares the basic token type used in the HTML DTD's.
* @update gess 3/25/98
*/
classCHTMLToken : public CToken {
public:
virtual ~CHTMLToken();
CHTMLToken(eHTMLTags aTag);
//全部都是虚函数,用于重载
virtual eContainerInfo GetContainerInfo(void)const {return eFormUnknown;}
virtual voidSetContainerInfo(eContainerInfo aInfo) { }
protected:
};
//下面我们来看非常重要的一个类CStartToken,顾名思义他是定义了起始型标签的数据结构,包括它所有的方法,属性等等。
/**
* Thisdeclares start tokens, which always take the form <xxxx>.
* Thisclass also knows how to consume related attributes.
*
* @update gess 3/25/98
*/
//下面的这段代码声明了起始型的Tokens,它们都是采用<xxxx>的格式。这个类同样还知道如何对相关属性进行解析。
classCStartToken: public CHTMLToken {
CTOKEN_IMPL_SIZEOF
public:
CStartToken(eHTMLTags aTag=eHTMLTag_unknown); //构造方法,参数为eHTMLTags,其值默认为未知类型
CStartToken(const nsAString&aString); //第二种构造方法,用字符串作为参数
CStartToken(const nsAString&aName,eHTMLTags aTag); //第三种构造方法,用字符串外带一个aTag作为参数。
//下面的全部都是虚方法
virtual nsresult Consume(PRUnicharaChar,nsScanner& aScanner,PRInt32 aMode); //解析扫描器中当前这段代码。
virtual PRInt32 GetTypeID(void); //获取类型ID
virtual PRInt32 GetTokenType(void); //获取Token的类型
virtual PRBool IsEmpty(void); //判断是否是空
virtual voidSetEmpty(PRBool aValue); //设置为空
virtual constnsSubstring& GetStringValue(); //获取字符串形式的值
virtual voidGetSource(nsString& anOutputString); //获取源代码,并输入到anOutputString中
virtual voidAppendSourceTo(nsAString& anOutputString); //添加源代码到anOutputString中
// the following info is used to set well-formedness stateon start tags...
//下面这段信息是用来设置起始型Tags上的良构状态的
virtual eContainerInfo GetContainerInfo(void)const {return mContainerInfo;}
//获取当前的容器信息
virtual voidSetContainerInfo(eContainerInfo aContainerInfo) {
//设置当前的容器信息,如果当前容器信息为未知类型的花,那么就将其进行设置,意思是说只有在类型未知的情况下才能对其进行设置
if (eFormUnknown==mContainerInfo) {
mContainerInfo=aContainerInfo;
}
}
//判断是否是良构的
virtual PRBool IsWellFormed(void)const {
return eWellFormed == mContainerInfo;
}
//注意这个数据成员是public的
nsString mTextValue;
protected:
eContainerInfo mContainerInfo; //存放当前Token的容器信息
PRPackedBool mEmpty; //存放当前Token是否是空
#ifdefDEBUG
PRPackedBool mAttributed;
#endif
};
//下面我们来看和上面这个起始型Token相对应的结束型Token。
/**
* Thisdeclares end tokens, which always take the
* form</xxxx>. This class also knows how to consume
* related attributes.
*
* @update gess 3/25/98
*/
//下面这段代码声明了结束型的Token,它们都是采用</xxxx>的形式。这个类同时还知道如何去解析相关的属性。
classCEndToken: public CHTMLToken {
CTOKEN_IMPL_SIZEOF
public:
CEndToken(eHTMLTags aTag); //构造方法
CEndToken(const nsAString& aString); //构造方法,用字符串做参数
CEndToken(const nsAString&aName,eHTMLTags aTag); //和前面的起始型Token的构造方法结构一样
virtual nsresult Consume(PRUnicharaChar,nsScanner& aScanner,PRInt32 aMode); //对当前扫描器中的字符进行解析
virtual PRInt32 GetTypeID(void); //获取类型ID
virtual PRInt32 GetTokenType(void); //获取Token的类型
virtual constnsSubstring& GetStringValue(); //获取字符值
virtual voidGetSource(nsString& anOutputString); //获取源代码
virtual voidAppendSourceTo(nsAString& anOutputString); //输出源代码
protected:
nsString mTextValue; //注意这个数据成员变成受保护类型的了,起始型标签的是public,可以思考一下为什么
};
//下面这个声明的是注释型标签
/**
* Thisdeclares comment tokens. Comments are usually
* thought of as tokens, but we treat them that way
* hereso that the parser can have a consistent view
* ofall tokens.
*
* @update gess 3/25/98
*/
//下面这段代码声明了注释型标签的数据结构。注释通常被当做Token来对待,但是我们在这里这样对他们进行处理,以便Parser能够对所有的Tokens都有一致的认识。
classCCommentToken: public CHTMLToken {
CTOKEN_IMPL_SIZEOF //参见nsToken.h文件,预先#define的一个语句
public:
CCommentToken(); //构造方法
CCommentToken(const nsAString&aString); //构造方法,采用字符串作为参数
virtual nsresult Consume(PRUnicharaChar,nsScanner& aScanner,PRInt32 aMode); //标准的解析方法,对扫描器当前的字符串进行解析
virtual PRInt32 GetTokenType(void); //获取Token的类型
virtual constnsSubstring& GetStringValue(void); //获取字符串类型的值
virtual voidAppendSourceTo(nsAString& anOutputString); //输出源代码到字符串
nsresult ConsumeStrictComment(nsScanner& aScanner); //解析strict类型的注释
nsresult ConsumeQuirksComment(nsScanner& aScanner); //解析quirks类型的注释
//上面的strict和quirks是根据DTD不同而不同的两种解析模式。
protected:
nsScannerSubstring mComment; // does notinclude MDO & MDC
nsScannerSubstring mCommentDecl; // includesMDO & MDC
};
//下面是用来存放实体类型的Token的类的声明。
/**
* Thisclass declares entity tokens, which always take
* theform &xxxx;. This class also offers a few utility
* methods that allow you to easily reduce entities.
*
* @update gess 3/25/98
*/
classCEntityToken : public CHTMLToken {
CTOKEN_IMPL_SIZEOF //预先的#define语句,请参见nsToken.h
public:
CEntityToken(); //构造方法
CEntityToken(const nsAString&aString); //构造方法,用字符串做为参数
virtual PRInt32 GetTokenType(void); //获取Token的类型
PRInt32 TranslateToUnicodeStr(nsString& aString); //将字符串编码转换为Unicode的方法
virtual nsresult Consume(PRUnicharaChar,nsScanner& aScanner,PRInt32 aMode); //主要解析方法,从当前扫描器的当前位置开始解析
static nsresult ConsumeEntity(PRUnichar aChar,nsString& aString,
nsScanner&aScanner); //解析实体用的方法,注意参数的区别
static PRInt32 TranslateToUnicodeStr(PRInt32aValue,nsString& aString);
//转换字符串为Unicode编码,注意参数和前面方法的不同
virtual constnsSubstring& GetStringValue(void); //获取字符串的值
virtual voidGetSource(nsString& anOutputString); //获取源代码
virtual voidAppendSourceTo(nsAString& anOutputString); //输出源代码到目标字符串
protected:
nsString mTextValue; //注意这里也是受保护的数据类型
};
//下面是存放空格字符类型Token的类。
/**
* Whitespace tokens are used where whitespace can be
* detected as distinct from text. This allows us to
* easily skip leading/trailing whitespace when desired.
*
* @update gess 3/25/98
*/
//空格类型字符的Token是在当空格能够被和文本区分地监测出来的时候所使用。这使得我们可以在需要的时候轻易地跳过那些开头/结尾的空格。
classCWhitespaceToken: public CHTMLToken {
CTOKEN_IMPL_SIZEOF //预先#define的语句,请参考nsToken.h
public:
CWhitespaceToken(); //构造方法
CWhitespaceToken(const nsAString&aString); //构造方法,用字符串作为参数
virtual nsresult Consume(PRUnicharaChar,nsScanner& aScanner,PRInt32 aMode); //主要解析方法
virtual PRInt32 GetTokenType(void); //获取Token的类型
virtual constnsSubstring& GetStringValue(void); //获取字符串的值
protected:
nsScannerSharedSubstring mTextValue; //受保护类型的数据成员,存放文本值
};
//下面是用来存放普通文本的Token的类的声明
/**
* Texttokens contain the normalized form of html text.
* Thesetokens are guaranteed not to contain entities,
* startor end tags, or newlines.
*
* @update gess 3/25/98
*/
//文本Tokens包含普通格式的html文本,这些Token保证不会包含任何的实体数据,起始或结束标签,或者新行。
classCTextToken: public CHTMLToken {
CTOKEN_IMPL_SIZEOF //预先#define的方法,请参见nsToken.h
public:
CTextToken(); //构造方法
CTextToken(const nsAString&aString); //使用字符串作为参数的构造方法
virtual nsresult Consume(PRUnicharaChar,nsScanner& aScanner,PRInt32 aMode);
//主要解析方法,从当前扫描器的当前位置开始进行解析
virtual PRInt32 GetTokenType(void); //获取Token的类型
virtual PRInt32 GetTextLength(void); //获取文本的长度
virtual voidCopyTo(nsAString& aStr); //拷贝文本到字符串
virtual constnsSubstring& GetStringValue(void); //获取字符串值
//绑定文本到扫描器的起始和结束位置
virtual voidBind(nsScanner* aScanner, nsScannerIterator& aStart,
nsScannerIterator&aEnd);
//绑定到字符串
virtual void Bind(const nsAString& aStr);
//解析字符类型的数据,注意参数的作用
nsresult ConsumeCharacterData(PRBool aIgnoreComments,
nsScanner&aScanner,
const nsAString& aEndTagName,
PRInt32 aFlag,
PRBool&aFlushTokens);
//解析经过预解析字符类型的数据,注意参数的作用,我们到具体实现的代码时再进行说明
nsresult ConsumeParsedCharacterData(PRBool aDiscardFirstNewline,
PRBoolaConservativeConsume,
nsScanner& aScanner,
constnsAString& aEndTagName,
PRInt32aFlag,
PRBool& aFound);
protected:
nsScannerSubstring mTextValue; //注意这个数据类型也是受保护的
};
//下面是用来存放[!CDATA],即连续型段文本的数据的类。
/**
* CDATASection tokens contain raw unescaped text content delimited by
* a![CDATA[ and ]].
* XXXNot really a HTML construct - maybe we need a separation
*
* @update vidur 11/12/98
*/
//CDATA类型的Token包含连续的段文本,它们由![CDATA[ 和 ]]符号进行划分,其实这并不能说是严格意义上的HTML结构—也许我们需要对他们进行一下区分
classCCDATASectionToken : public CHTMLToken {
CTOKEN_IMPL_SIZEOF
public:
CCDATASectionToken(eHTMLTags aTag = eHTMLTag_unknown); //构造方法
CCDATASectionToken(const nsAString&aString); //构造方法
virtual nsresult Consume(PRUnicharaChar,nsScanner& aScanner,PRInt32 aMode); //主要解析方法,从当前解析器的当前位置开始进行解析
virtual PRInt32 GetTokenType(void); //获取当前Token的类型
virtual constnsSubstring& GetStringValue(void); //获取字符串的值
protected:
nsString mTextValue; //受保护类型的数据成员,存放当前文本值
};
//下面的类用来存放一些声明型的标签,他们也是被认为是连续型的段文本。
/**
* Declaration tokens contain raw unescaped text content (not really, but
* rightnow we use this only for view source).
* XXXNot really a HTML construct - maybe we need a separation
*
*/
//声明类型的Tokens包含连续的纯文本内容(其实并不一定,但是我们目前只有在阅览源代码方式时来使用它)
//并不是一个HTML结构—也许我们需要对其进行一下区分
classCMarkupDeclToken : public CHTMLToken {
CTOKEN_IMPL_SIZEOF //预先#define的方法,详情请见nsToken.h
public:
CMarkupDeclToken(); //构造方法
CMarkupDeclToken(const nsAString&aString); //构造方法
virtual nsresult Consume(PRUnicharaChar,nsScanner& aScanner,PRInt32 aMode); //解析方法,从当前解析器的当前位置开始进行解析
virtual PRInt32 GetTokenType(void); //获取Token类型
virtual constnsSubstring& GetStringValue(void); //获取字符串的值
protected:
nsScannerSubstring mTextValue; //用来存放当前文本的值
};
//下面是用来存放属性的类。
/**
* Attribute tokens are used to contain attribute key/value
* pairswhereever they may occur. Typically, they should
* occuronly in start tokens. However, we may expand that
* ability when XML tokens become commonplace.
*
* @update gess 3/25/98
*/
//属性tokens是用来包含属性的,其中需要存储属性名/属性值的序对。普通情况下,他们应当只在起始型Tokens中出现。然而,我们可能在今后遇到XML Token的时候扩展这一功能。
classCAttributeToken: public CHTMLToken {
CTOKEN_IMPL_SIZEOF //预先#define的代码,详情请参见nsToken.h
public:
CAttributeToken(); //构造方法
CAttributeToken(const nsAString&aString); //构造方法
CAttributeToken(const nsAString&aKey,const nsAString& aString); //构造方法
~CAttributeToken() {} //析构方法
virtual nsresult Consume(PRUnicharaChar,nsScanner& aScanner,PRInt32 aMode); //主要解析方法,用来解析当前扫描器的当前位置的值
virtual PRInt32 GetTokenType(void); //获取Token的类型
const nsSubstring& GetKey(void){return mTextKey.AsString(); } //获取Key,即属性名的值
virtual void SetKey(const nsAString& aKey); //设置key,即属性名的值
//绑定Key至扫描器
virtual voidBindKey(nsScanner* aScanner, nsScannerIterator& aStart,
nsScannerIterator&aEnd);
//获取值,获取当前的文本值
const nsSubstring& GetValue(void) {returnmTextValue.str();}
//获取字符串的值
virtual constnsSubstring& GetStringValue(void);
//输出源代码到字符串
virtual voidGetSource(nsString& anOutputString);
//输出源代码到字符串的尾端
virtual voidAppendSourceTo(nsAString& anOutputString);
PRPackedBool mHasEqualWithoutValue; //一个BOOL判断位,从字面上来看应该是判断该属性是否是没有赋值的情况。
protected:
nsScannerSharedSubstring mTextValue; //当前的文本值
nsScannerSubstring mTextKey; //当前的属性名
};
//下面是一个用来存放换行符的类的声明。
/**
* Newline tokens contain, you guessed it, newlines.
* Theyconsume newline (CR/LF) either alone or in pairs.
*
* @update gess 3/25/98
*/
//换行符包含,你猜是什么,当然是换行符。
//他们包含成对或者单独的换行符(CR/LF)
classCNewlineToken: public CHTMLToken {
CTOKEN_IMPL_SIZEOF //预先#define的值,详情参见nsToken.h
public:
CNewlineToken(); //构造方法
virtual nsresult Consume(PRUnicharaChar,nsScanner& aScanner,PRInt32 aMode); //主要的解析方法,从当前扫描器的当前位置进行扫描
virtual PRInt32 GetTokenType(void); //获取Token的类型
virtual constnsSubstring& GetStringValue(void); //获取字符串的值
static void AllocNewline(); //分配新行
static voidFreeNewline(); //回收新行
};
//下面是用来存放指令类型的Token。
/**
* Whitespace tokens are used where whitespace can be
* detected as distinct from text. This allows us to
* easily skip leading/trailing whitespace when desired.
*
* @update gess 3/25/98
*/
//和空格字符Token的代码注释重复了
classCInstructionToken: public CHTMLToken {
CTOKEN_IMPL_SIZEOF
public:
CInstructionToken();
CInstructionToken(const nsAString&aString);
virtual nsresult Consume(PRUnicharaChar,nsScanner& aScanner,PRInt32 aMode);
virtual PRInt32 GetTokenType(void);
virtual constnsSubstring& GetStringValue(void);
protected:
nsString mTextValue;
};
//最后一个,就是用来对DOCTYPE进行存放的:
/**
* This token is generated by the HTML andExpat tokenizers
* when they see the doctype declaration("<!DOCTYPE ... >")
*
*/
//这个Token是当HTML和Expat分词器看到doctype声明(”<!DOCTYPE…>”)的时候自动生成的
classCDoctypeDeclToken: public CHTMLToken {
CTOKEN_IMPL_SIZEOF //预先#define的语句,详情请查看nsToken.h文件
public:
CDoctypeDeclToken(eHTMLTags aTag=eHTMLTag_unknown); //构造方法
CDoctypeDeclToken(const nsAString&aString,eHTMLTags aTag=eHTMLTag_unknown); //构造方法
virtual nsresult Consume(PRUnicharaChar,nsScanner& aScanner,PRInt32 aMode); //主要解析方法,用来从当前的扫描器的当前位置开始进行解析
virtual PRInt32 GetTokenType(void); //获取Token的类型
virtual constnsSubstring& GetStringValue(void); //获取字符串的值
virtual voidSetStringValue(const nsAString& aStr); //设置字符串的值
protected:
nsString mTextValue; //受保护的数据类型,用来存放字符值
};
#endif
以上就是所有nsHTMLToken.h头文件的内容了,可以看到,它主要采用了一个枚举类型集合来声明了所有的HTML标签的类型,这个枚举类型主要用在Allocator中创建并为每个新Token分配内存的时候所使用,并且对每个标签类型定义了它们的基本方法和数据成员,下面,我们就来看看这些方法的具体实现和数据成员的使用。
下面是nsHTMLToken.cpp文件的源代码解析:
#include <ctype.h>
#include <time.h>
#include <stdio.h>
#include "nsScanner.h"
#include "nsToken.h"
include "nsHTMLTokens.h"
#include "prtypes.h"
#include "nsDebug.h"
#include "nsHTMLTags.h"
#include "nsHTMLEntities.h"
#include "nsCRT.h"
#include "nsReadableUtils.h"
#include "