当前位置: 代码迷 >> Web前端 >> Android webkit image的加载进程解析
  详细解决方案

Android webkit image的加载进程解析

热度:821   发布时间:2013-10-30 12:56:21.0
Android webkit image的加载过程解析

#############################################

本文为极度寒冰原创,转载请注明出处
#############################################


webkit有一个预下载的功能,主要是对img,script,link,input的标签的内容进行预下载。
我们知道了预下载的下载过程,但是预下载的内容是怎么被读取的呢?
这部分准备用两天的时间进行研究。
读取一个预下载的内容,准备从img标签开始进行研究。原因是img的内容我们比较直观,相比较于script我们也更加的容易理解.


前面的分析可以得到,img的标签主要对应的是HTMLImage方面的内容,在html的目录下找到了这两个文件。HTMLImageLoader与HTMLImageElement
分析还是从我们熟悉的堆栈开始:
#0  WebCore::ImageLoader::updateFromElement (this=0x2acef4ec) at external/webkit/Source/WebCore/loader/ImageLoader.cpp:163
#1  0x48d2dc6c in WebCore::HTMLImageElement::parseMappedAttribute (this=0x2acef4b0, attr=0x2ab94720) at external/webkit/Source/WebCore/html/HTMLImageElement.cpp:108
#2  0x48efb706 in WebCore::StyledElement::attributeChanged (this=0x2acef4b0, attr=0x2ab94720, preserveDecls=<value optimized out>) at external/webkit/Source/WebCore/dom/StyledElement.cpp:187
#3  0x48ef0d4a in WebCore::Element::setAttributeMap (this=0x2acef4b0, list=<value optimized out>, scriptingPermission=<value optimized out>) at external/webkit/Source/WebCore/dom/Element.cpp:844
#4  0x48fc5f7a in WebCore::HTMLConstructionSite::createHTMLElement (this=0x2ab4b60c, token=...) at external/webkit/Source/WebCore/html/parser/HTMLConstructionSite.cpp:380
#5  0x48fc6476 in WebCore::HTMLConstructionSite::insertSelfClosingHTMLElement (this=0x2ab4b60c, token=<value optimized out>) at external/webkit/Source/WebCore/html/parser/HTMLConstructionSite.cpp:296
#6  0x48f2f908 in WebCore::HTMLTreeBuilder::processStartTagForInBody (this=0x2ab4b5f8, token=...) at external/webkit/Source/WebCore/html/parser/HTMLTreeBuilder.cpp:929
#7  0x48f30c5a in WebCore::HTMLTreeBuilder::processStartTag (this=0x2ab4b5f8, token=...) at external/webkit/Source/WebCore/html/parser/HTMLTreeBuilder.cpp:1335
#8  0x48f323d4 in WebCore::HTMLTreeBuilder::constructTreeFromAtomicToken (this=0x2ab4b5f8, token=<value optimized out>) at external/webkit/Source/WebCore/html/parser/HTMLTreeBuilder.cpp:461
#9  0x48f3250a in WebCore::HTMLTreeBuilder::constructTreeFromToken (this=0x2ab4b5f8, rawToken=...) at external/webkit/Source/WebCore/html/parser/HTMLTreeBuilder.cpp:451
#10 0x48f26aba in WebCore::HTMLDocumentParser::pumpTokenizer (this=0x2ab94770, mode=WebCore::HTMLDocumentParser::AllowYield) at external/webkit/Source/WebCore/html/parser/HTMLDocumentParser.cpp:276
#11 0x48f26b4e in WebCore::HTMLDocumentParser::resumeParsingAfterYield (this=0x2ab94770) at external/webkit/Source/WebCore/html/parser/HTMLDocumentParser.cpp:192




WebCore::HTMLDocumentParser::pumpTokenizer -> WebCore::HTMLTreeBuilder::constructTreeFromToken -> WebCore::HTMLTreeBuilder::constructTreeFromAtomicToken -> HTMLTreeBuilder::processToken -> WebCore::HTMLTreeBuilder::processStartTag -> WebCore::HTMLTreeBuilder::processStartTagForInBody
到这个标签为止,还是比较熟悉的.  但是接下来的几个stack,我们还得一个一个去研究.


首先processStartTagForInBody是告诉我们现在已经进入到了body的标签中,而body标签包含了当前的文档的所有内容(包括文本,超链接,图像,表格等等).
然后呢,在body里面的处理,会进行insertSelfClosingHTMLElement的处理。
这个insertSelfClosingHTMLElement一般都会出现在什么地方呢?
我们可以看到主要是出现在下面几个tag的处理中:
   if (token.name() == areaTag
        || token.name() == brTag
        || token.name() == embedTag
        || token.name() == imgTag
        || token.name() == keygenTag
        || token.name() == wbrTag) {
        m_tree.reconstructTheActiveFormattingElements();
        m_tree.insertSelfClosingHTMLElement(token);
        m_framesetOk = false;
        return;
   }
   if (token.name() == inputTag) {
       RefPtr<Attribute> typeAttribute = token.getAttributeItem(typeAttr);
       m_tree.reconstructTheActiveFormattingElements();
       m_tree.insertSelfClosingHTMLElement(token);
       if (!typeAttribute || !equalIgnoringCase(typeAttribute->value(), "hidden"))
           m_framesetOk = false;
       return;
   }
   if (token.name() == paramTag
       || token.name() == sourceTag
       || token.name() == trackTag) {
       m_tree.insertSelfClosingHTMLElement(token);
       return;
   }
   if (token.name() == hrTag) {
       processFakePEndTagIfPInButtonScope();
       m_tree.insertSelfClosingHTMLElement(token);
       m_framesetOk = false;
       return;
   }


可以看到,在当前标签的tag为<area> <br> <embed> <img> <keygen> <wbr> <input> <param> <hr>的时候,我们都会调用到这个方法。
因为我们这边主要讨论的是image的情况,所以在这边也主要研究img的标签。
而insertSelfClosingHTMLElement的具体实现是怎么样的呢?
去除掉无关的注释和ASSERT判断后,我们可以看到这个函数的处理其实很简单。
void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken& token)
{
    RefPtr<Element> element = attachToCurrent(createHTMLElement(token));
    element->finishParsingChildren();
}


createHTMLElement我们前面分析过,这个函数的主要作用是通过HTMLElementFactory和依据tagName,一起去创建一个当前的element。
而创建完element之后,element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission)就会进行setAttributeMap的操作。
setAttributeMap函数主要进行的就是Element中设置的相应的属性.
在这个函数中,当期间有属性的添加,修改,删除时都会使用到StyledElement::attributeChanged来进行通知并更新该属性的情况.


// parseMappedAttribute() might create a CSSMappedAttributeDeclaration on the attribute.
// Normally we would be concerned about reseting the parent of those declarations in StyledElement::didMoveToNewOwnerDocument().
// But currently we always clear its parent and node below when adding it to the decl table.
// If that changes for some reason moving between documents will be buggy.
// webarchive/adopt-attribute-styled-node-webarchive.html should catch any resulting crashes.
if (needToParse)
    parseMappedAttribute(attr);



这边的话,当前属性如果改变或者解析到需要解析的标签的时候needToParse是置为true的,也就进入了这个判断。
parseMappedAttribute是一个虚函数,也就是在每一个需要实现的Element中都会进行实现。
这边我们跟踪的是一个img的标签,所以理所当然也就进入了一个HTMLImageElement::parseMappedAttribute的函数进行操作。
HTMLImageElement中,我们跟踪的网页是这么写的。
    <h1 style="color:red"> Chao </h1>
        <img class="x" src="chao.jpg">
    <p style="color:white">Chao's photo</p>


而且在一般的规则中,image的资源文件都是以src来作为标签。
所以我们就进入如下判断进行下一步的处理:
else if (attrName == srcAttr)
       m_imageLoader.updateFromElementIgnoringPreviousError();

这样就进入了imageLoader的操作中。

void ImageLoader::updateFromElementIgnoringPreviousError()
{
    // Clear previous error.
    m_failedLoadURL = AtomicString();
    updateFromElement();
}



到这边为止,我们也就看到了我们需要寻找的updateFromElement()的操作。
这个函数前面的文章已经有过了介绍,这边只是再次复习一下这边perload的操作,
void ImageLoader::updateFromElement()
{
    ....
    //重要:获取 src属性的值
    AtomicString attr = client()->sourceElement()->getAttribute(client()->sourceElement()->imageSourceAttributeName());


   ...
    // Do not load any image if the 'src' attribute is missing or if it is
    // an empty string.
    CachedResourceHandle<CachedImage> newImage = 0;
    if (!attr.isNull() && !stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) {
        //重要:根据url(attr的值),创建一个request,通过该request获取图片
        CachedResourceRequest request(ResourceRequest(document()->completeURL(sourceURI(attr))));
        request.setInitiator(client()->sourceElement());


      .....
        if (m_loadManually) {
            bool autoLoadOtherImages = document()->cachedResourceLoader()->autoLoadImages();
           <strong> </strong>document()->cachedResourceLoader()->setAutoLoadImages(false);
            newImage = new CachedImage(request.resourceRequest()); //创建image对象
            newImage->setLoading(true);
            newImage->setOwningCachedResourceLoader(document()->cachedResourceLoader());
            document()->cachedResourceLoader()->m_documentResources.set(newImage->url(), newImage.get());
            document()->cachedResourceLoader()->setAutoLoadImages(autoLoadOtherImages);
        } else
            newImage = document()->cachedResourceLoader()->requestImage(request); //直接获取一个cached的image对象


       ....
}



这篇文章的分析就到此结束,大致讲述了一个img文件的加载过程。


当然,加载之后的处理是非常非常复杂的,接下来会继续进行分析。
  相关解决方案