chromium DOM树形成浅析

转载,请标注: from 你吧吧
当主资源load下来,我们来看看chromium如何去解析主资源,构建DOM树的。

整个DOM的创建过程比较复杂,这里简单分析下,如何将网络中download的资源,进行DOM树的解析构建过程的。

 

当主资源加载完,准确的讲,在主资源还没加载完,但是sandbox process已经收到从 browser process的部分主资源内容的时候,就开始了DOM树的构建。

我们这里从“sandbox process已经收到从 browser process的部分主资源内容”,这个时刻开始说起。

1. ResourceLoader.cpp文件的方法:ResourceLoader::didReceiveData,这是sandbox process收到了部分主资源

    该方法中有代码:m_resource->appendData(data, length);

2. 上代码执行的是文件:RawResource.cpp中方法:RawResource::appendData

    该方法中有代码:

        while (RawResourceClient* c = w.next())
        c->dataReceived(this, data, length);

3. 这里会调用DocumentLoader.cpp文件中方法:DocumentLoader::dataReceived

    该方法中有代码:commitData(data, length);

    执行的是同文件中方法:DocumentLoader::commitData

4. 上面提到的方法中有代码:m_writer->addData(bytes, length);

     执行的是文件DocumentWriter.cpp文件中方法:DocumentWriter::addData

     该方法中有代码:m_parser->appendBytes(bytes, length);

    执行的是文件HTMLDocumentParser.cpp中方法:HTMLDocumentParser::appendBytes

   从这个方法开始,则开始了复杂的Dom树的解析过程。


  总体上说,从网络中读到的主资源以字符串的形式存在内存中,chromium内核(或者说blink内核)
如何将这些字符串流分析成一个个html 的标签呢?我们看下面的逻辑分析。

5. 在方法:HTMLDocumentParser::appendBytes中有代码:

HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::appendRawBytesFromMainThread, m_backgroundParser, buffer.release()));

  这里执行的调用文件  BackgroundHTMLParser.cpp中方法:BackgroundHTMLParser::appendRawBytesFromMainThread


6. 该方法又会调用同文件中方法:BackgroundHTMLParser::updateDocument。该方法中有代码:

    appendDecodedBytes(decodedData);

    该代码调用的是同文件种方法:BackgroundHTMLParser::appendDecodedBytes

    我们看看该方法:

   

void BackgroundHTMLParser::appendDecodedBytes(const String& input)
{
    ASSERT(!m_input.current().isClosed());
    m_input.append(input);
    pumpTokenizer();
}

   这里会将多个主资源部分 都保存在变量 m_input中。

   并调用同文件中方法:BackgroundHTMLParser::pumpTokenizer()

7. 上面的这个方法比较关键,贴出源码看看

void BackgroundHTMLParser::pumpTokenizer()
{
       // No need to start speculating until the main thread has almost caught up.
    if (m_input.totalCheckpointTokenCount() > outstandingTokenLimit)
        return;

    while (true) {
        m_sourceTracker.start(m_input.current(), m_tokenizer.get(), *m_token);
        if (!m_tokenizer->nextToken(m_input.current(), *m_token)) {
            WTF_LOG(Vnbo,"vnbo BackgroundHTMLParser::pumpTokenizer() 1");
            // We've reached the end of our current input.
            sendTokensToMainThread();
            break;
        }
        m_sourceTracker.end(m_input.current(), m_tokenizer.get(), *m_token);

        {
            TextPosition position = TextPosition(m_input.current().currentLine(), m_input.current().currentColumn());

            if (OwnPtr<XSSInfo> xssInfo = m_xssAuditor->filterToken(FilterTokenRequest(*m_token, m_sourceTracker, m_tokenizer->shouldAllowCDATA()))) {
                xssInfo->m_textPosition = position;
                m_pendingXSSInfos.append(xssInfo.release());
            }

            CompactHTMLToken token(m_token.get(), TextPosition(m_input.current().currentLine(), m_input.current().currentColumn()));

            m_preloadScanner->scan(token, m_input.current(), m_pendingPreloads);

            m_pendingTokens->append(token);

        }

        m_token->clear();

        if (!m_treeBuilderSimulator.simulate(m_pendingTokens->last(), m_tokenizer.get()) || m_pendingTokens->size() >= pendingTokenLimit) {
            sendTokensToMainThread();
            // If we're far ahead of the main thread, yield for a bit to avoid consuming too much memory.
            if (m_input.totalCheckpointTokenCount() > outstandingTokenLimit)
                break;
        }
    }
}
我们看看这个true循环中的逻辑。

在true循环中,会一直调用:HTMLSourceTracker.cpp中方法:HTMLSourceTracker::start和HTMLSourceTracker::end以及文件HTMLTokenizer.cpp中方法:HTMLTokenizer::nextToken

其大体逻辑是如此:

     1). 从输入的字符串中依次读取,当读到<字符和>字符的时候,内核认为这两个符号之间的内容是一个自然字符串段。并记载下解析到的位置(文件HTMLToken.h中方法:setBaseOffset)。下次读取的时候,再从输入字符串的该位置读取。

     2). 遇到<字符,,则认为是一个新的标签开始;遇到/>字符,则认为与之前的<字符之间的内容是一个标签。

     3). 标签之间存在父子关系。

网上有个blog:http://blog.csdn.net/bertzhang/article/details/6695804 对这一点有较好的讲解。

     4). 在主资源的解析过程中,遇到标签如 <script>、<img>标签等等,则会异步加载子资源。

8. true循环中还会CompactHTMLToken对象,然后将所有CompactHTMLToken对象保存到m_pendingTokens变量中。

    当所有标签都打上token之后,会调用同文件中方法:BackgroundHTMLParser::sendTokensToMainThread()

    并且跳出true循环。

9. 上面提到的方法,会调用文件:HTMLDocumentParser.cpp中方法:

    HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser

10. 上面方法会调用同文件中方法:HTMLDocumentParser::pumpPendingSpeculations()

      该方法会调用同文件中方法:HTMLDocumentParser::processParsedChunkFromBackgroundParser

11. 上面方法会调用同文件种方法:HTMLDocumentParser::constructTreeFromCompactHTMLToken

       在这个方法中有代码:m_treeBuilder->constructTree(&token);

12. 上面代码执行的是文件HTMLTreeBuilder.cpp中方法HTMLTreeBuilder::constructTree。

        该方法会执行同文件中方法:HTMLTreeBuilder::processToken

        标签的开始会执行该方法中代码:

    case HTMLToken::StartTag:
        processStartTag(token);
        break;

      标签的结束会执行该方法中代码:

    case HTMLToken::EndTag:
        processEndTag(token);
        break;

13.我们先看标签开始的逻辑。标签开始会执行同文件中方法:HTMLTreeBuilder::processStartTag

    在该方法中,我们看到有很多mode:InitialMode、BeforeHTMLMode、BeforeHeadMode、InHeadMode、AfterHeadMode、InBodyMode、InTableMode、InCaptionMode、InColumnGroupMode、InTableBodyMode等等。不知道这里的这些Mode作用是什么。这个需要以后去探讨研究下。

    我这里还是在ipad ua下加载的百度首页。这里执行的是InBodyMode。该模式下调用的是同文件中方法:HTMLTreeBuilder::processStartTagForInBody

14. 在方法HTMLTreeBuilder::processStartTagForInBody,则开始了对所有的AtomicHTMLToken对象分类处理,并且保存到HTMLConstructionSite对象中。




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值