chromium:LoadUrl之后会发生那些事情之DOMTree创建

一 综述 

上一篇分析从地址栏输入网址,Application --> content-->blink-->Network stack到调用网络库从网络上进行资源下载,这篇将分析从resource得到资源后,再到如何建立DOM Tree的完整过程。

在上一篇“资源下载”中进分析资源下载的整个流程,并未涉及到页面的相关结构,一个页面blink对外部(content)是WebView,而对内部是Page,每个Page都会有Main Frame,也可以存在零个或多个Sub Frame,每个Frame都有自己的DocumentDocument中包含各种各样的资源(resource),它的结构如图1所示。

 

页面结构

因此,我们再前一篇看到,在资源下载过程中,是依次创建或调用FrameLoaderDocumentLoader,还有ResourceLoader

DocumentLoaderstartLoadingMainResource()中,会执行m_mainResource = m_fetcher->fetchMainResource(cachedResourceRequest, m_substituteData);

开始主文档资源下载,接着执行m_mainResource->addClient(this);m_mainResource 就是RawResource。这里的this 就是指DocumentLoader DocumentLoader是继承于RawResourceClient的子类,当RawResource从网络上获取到数据后,会调用RawResourceClinet->dataReceived(this, data, length);

这里就会走到DocumentLoader中的dataReceived(),接着调用到commitData(data, length),

commitData()中做了两件很重要的事情,

#1 创建DocumentWriterensureWriter(m_response.mimeType()); 

#2 DocumentWriter中添加数据:m_writer->addData(bytes, length);

它会把这些数据给DocumentParser处理,调用的是m_parser->appendBytes(bytes, length);

ensureWriter()继续会调用DocumentLoader::createWriterFor(),这里会根据mimeType创建具体哪些文档类型的Document,最常见的Document类型是XMLXHTML)和HTML

createWriterFor()有个重要的参数ParserSynchronizationPolicy,创建DocumentWriter后,DocumentWriter会继续创建DocumentParser,这是一个基类,它会根据解析不同类型文档

,创建不同的DocumentParser,因为这里是HTML文档,所以它创建的DocumentParserHTMLDocumentParser。创建DOMTreeHTMLDocumentParser的主要工作。

下面来看看DOMTree是什么样的,假如有如下图图2是一段非常简单的html页面:

 

图2 测试页面

有了html页面,下来再blink添加一些简单的接口(dumpDOMTree)就可以将其DOMTree打印出来,如果为了方便调试,可以将这些接口(dumpDOMTree)添加到Document上,这样可以在chrome://inspect的远程调试中调用,结果如下图3所示。

 

图3 测试页面的DOM Tree

其实,图3中的DOMTree结构已经非常清晰了,根节点是document,它的子节点HTMLHTML有两个子节点分别是HEADBODYBODY有两个子节点都是DIV,相同缩进的节点在tree结构中属于同一层。如果把图3内容有一颗树来表示是这样的,实际上,html页面与其DOMTree是一一完全对应的,如图4所示。

 

4 DOM Tree

下面来看看具体的代码流程,在HTMLDocumentParser中,有

OwnPtr<HTMLToken> m_token;

OwnPtr<HTMLTokenizer> m_tokenizer;

OwnPtrWillBeMember<HTMLTreeBuilder> m_treeBuilder;

主要是用来创建DOMTree,还有

OwnPtr<HTMLParserScheduler> m_parserScheduler;

WeakPtrFactory<HTMLDocumentParser> m_weakFactory;

WeakPtr<BackgroundHTMLParser> m_backgroundParser;

这里会创建一个后台线程m_backgroundParser,用来处理一些解析任务。主要有两个任务:

#1 把网络上获取的资源一段段数据解码后变成tokens;这个是在BackgroundHTMLParser::pumpTokenizer()

中进行处理,把当前获取的一段数据处理完,就把处理好的tokens通过sendTokensToMainThread()发送到主线程,继续处理;

#2 sendTokensToMainThread()会把数据打包到ParsedChunk中,然后

发给主线程HTMLDocumentParser::processParsedChunkFromBackgroundParserPassOwnPtr<ParsedChunk> popChunk)进行后续处理。

现在主要看HTMLDocumentParser::processParsedChunkFromBackgroundParserPassOwnPtr<ParsedChunk> popChunk),每次它都会解析一段数据,

popChunk中包含一个tokens的列表,做一个循环,对每一个token进行解析:

for (Vector<CompactHTMLToken>::const_iterator it = tokens->begin(); it != tokens->end(); ++it) {

......

  constructTreeFromCompactHTMLToken(*it);

.....

}

constructTreeFromCompactHTMLToken()

--->HTMLTreeBuilder::constructTree()m_treeBuilder->constructTree(&token); 

---->HTMLTreeBuilder::constructTree(AtomicHTMLToken* token);

---> HTMLTreeBuilder::processToken(AtomicHTMLToken* token):

processToken()函数内部结构非常清晰:

396 void HTMLTreeBuilder::processToken(AtomicHTMLToken* token)

 397 {

 398     if (token->type() == HTMLToken::Character) {

 399         processCharacter(token);

 400         return;

 401     }

 402 

 403     // Any non-character token needs to cause us to flush any pending text immediately.

 404     // NOTE: flush() can cause any queued tasks to execute, possibly re-entering the parser.

 405     m_tree.flush(FlushAlways);

 406     m_shouldSkipLeadingNewline = false;

 407 

 408     switch (token->type()) {

 409     case HTMLToken::Uninitialized:

 410     case HTMLToken::Character:

 411         ASSERT_NOT_REACHED();

 412         break;

 413     case HTMLToken::DOCTYPE:

 414         processDoctypeToken(token);                                                                                                                                                

 415         break;

 416     case HTMLToken::StartTag:

 417         processStartTag(token);

 418         break;

 419     case HTMLToken::EndTag:

 420         processEndTag(token);

 421         break;

 422     case HTMLToken::Comment:

 423         processComment(token);

 424         break;

 425     case HTMLToken::EndOfFile:

 426         processEndOfFile(token);

 427         break;

 428     }

 429 }

 

 版权声明:本文为博主原创文章,未经博主允许不得转载。


参考文献:

1  http://www.w3.org/DOM/

2  http://www.w3.org/TR/DOM-Level-3-Core/core.html

3  http://wenku.baidu.com/view/7fa3ad6e58fafab069dc02b8.html

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值