您的位置:首页 > 移动开发 > Android开发

Android webkit image的加载过程解析 (一)

2013-10-29 14:02 507 查看
#############################################

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

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

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文件的加载过程。

当然,加载之后的处理是非常非常复杂的,接下来会继续进行分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: