webkit内核源代码导读5: CSS如何作用到Render树上

来源:互联网 发布:手机淘宝在哪看等级 编辑:程序博客网 时间:2024/05/04 15:31

本章的目的,主要说明CSS相关的类和关系,还不能做到对其过程和原理的探究。后期我们慢慢会涉及。


CSS的主要作用,是修饰DOM的外观和排版的,它必须和Render--DOM的渲染对象结合起来。

在Render中,有一个重要的对象,是RenderStyle。这个RenderStyle是从CSS中创建出来的。


RenderStyle和StyleResolver

RenderStyle保存了Render树绘制所需要的全部信息。至于Render树如何使用Render,这里我们不仔细探索,我想重点探索一下StyleResolver。


StyleResolver将CSS的内容转换为RenderStyle,有几个函数是重点关注的:

    PassRefPtr<RenderStyle> styleForElement(Element*, RenderStyle* parentStyle = 0, StyleSharingBehavior = AllowStyleSharing,        RuleMatchingBehavior = MatchAllRules, RenderRegion* regionForStyling = 0);      ......    PassRefPtr<RenderStyle> pseudoStyleForElement(PseudoId, Element*, RenderStyle* parentStyle);    PassRefPtr<RenderStyle> styleForPage(int pageIndex);    PassRefPtr<RenderStyle> defaultStyleForElement();    PassRefPtr<RenderStyle> styleForText(Text*);    static PassRefPtr<RenderStyle> styleForDocument(Document*, CSSFontSelector* = 0); 
这里面几个styleForXXX函数,是产生RenderStyle的重要地方。


StyleResolver的关系

StyleResolver是Document的子对象。Document负责创建它,别的数据想要使用StyleResolver,需要调用 document()->styleResolver();

在Document.h中 (class Document)

    StyleResolver* styleResolver()    {        if (!m_styleResolver)            createStyleResolver();        return m_styleResolver.get();    }

StyleResolver的styleForXXX系列函数会在不同的地方调用。例如,styleForElement,会在Element::styleForRender中调用,而Element::styleForRender则会在recalcStyle(包括Document::recalcStyle和Element::recalcStyle)中调用。


recalcStyle函数是当CSS发生变化的时候被调用。例如,Document::updateStyleIfNeeded。因为调用的地方很多,不再一一列举。只需明白:当style发生变化后,就会引起recalcStyle


styleForElement:CSS选择和匹配

回到StyleResolver中看styleForElement,我们看看CSS的Selector是如何工作的。

styleForElement的代码片段

//将元素暂时保存在StyleResolver类中1534 1535     initElement(element);1536     initForStyleResolve(element, defaultParent);....//进行规则匹配1570     MatchResult matchResult;1571     if (matchingBehavior == MatchOnlyUserAgentRules)1572         matchUARules(matchResult);1573     else1574         matchAllRules(matchResult, matchingBehavior != MatchAllRulesExcludingSMIL);1575 //将获取的规则映射到RenderStyle中1576     applyMatchedProperties(matchResult, element);

选择和匹配的关键函数在matchUARules和matchAllRules两个函数中。

这里关键看函数matchUARules。该函数有两个重载,下面这个是最终实现

 825 void StyleResolver::matchUARules(MatchResult& result, RuleSet* rules) 826 {    827     m_matchedRules.clear(); 828      829     result.ranges.lastUARule = result.matchedProperties.size() - 1; 830     collectMatchingRules(rules, result.ranges.firstUARule, result.ranges.lastUARule, false); 831          832     sortAndTransferMatchedRules(result); 833 }   

注意函数collectMatchRulessortAndTransferMatchedRules。这两个函数,一个是收集选装的规则,一个是对规则进行排序。

那么,RuleSet是什么呢?

RuleSet就代表一个CSS规则,CSS规则包括CSS选择子和CSS描述。例如

p { background : red; }

这就是一个RuleSet。

RuleSet的定义中,有几个成员变量值得一看:

class RuleSet ... {.....132     AtomRuleMap m_idRules;133     AtomRuleMap m_classRules;134     AtomRuleMap m_tagRules;135     AtomRuleMap m_shadowPseudoElementRules;...};

显然,这几个是对应CSS的id, class, tag等选择子的,这几个选择子是最经常使用的,因此,webkit把他们单独拿出来做了优化。


那么collectMatchRules也就不难实现了。具体的实现,我们不再详细说明了。


对于sortAndTransferMatchedRules将获取到的ruleset进行排序,以保证正确的匹配顺序,否则,会引起CSS的显示错误。它是按照CSS的标准文档进行排序的,这一点,我们以后在详细考虑。


RuleSet的来源

RuleSet的来源很多,包括默认的RuleSet,这些是浏览器定义的。Author的RuleSet,这些是网页的作者定义的。对于Element,还有来自Element本身的规则。

Element的RuleSet来源

只有继承自StyledElement的Element才能提供RuleSet。这些就是所谓的内联CSS,如,

<p background="red"> ...<p style="background:red;">...
这样形式的CSS。

这类CSS的生成,是在StyledElement::rebuildPresentationAttributeStyle中完成的。该函数会在element的AttributeData发生变化后,调用。我们看,该函数是如何生成CSS的RuleSet的:

void StyledElement::rebuildPresentationAttributeStyle(){.....    RefPtr<StylePropertySet> style;    if (cacheHash && cacheIterator->value) {        style = cacheIterator->value->value;        presentationAttributeCacheCleaner().didHitPresentationAttributeCache();    } else {        style = StylePropertySet::create(isSVGElement() ? SVGAttributeMode : CSSQuirksMode);        unsigned size = attributeCount();        for (unsigned i = 0; i < size; ++i) {            const Attribute* attribute = attributeItem(i);            collectStyleForPresentationAttribute(*attribute, style.get());        }    }    // ImmutableElementAttributeData doesn't store presentation attribute style, so make sure we have a MutableElementAttributeData.    ElementAttributeData* attributeData = mutableAttributeData();    attributeData->m_presentationAttributeStyleIsDirty = false;    attributeData->setPresentationAttributeStyle(style->isEmpty() ? 0 : style);....

请关注:collectStyleForPresentationAttribute函数和attributeData->setProsentationAttributeStyle两个函数。

collectStyleForPresentationAttribute函数是个虚函数,需要被子类实现。子类需要判断那些属性是style属性,然后将他们的值生成为StyleProperty对象。

attributeData是Element保存属性的对象,这个对象为 ElementAttributeData。

这个类使用了一些技巧,以节省内存,这一点在阅读时,需要注意。


StyleResolver通过调用StyledElement::presentationAttributeStyle->ElementAttributeData::presentationAttributeStyle()函数,得到上面setProsentationAttributeStyle的结果。


AuthorStyle的来源

StyleResolver::m_authorStyle保存了当前网页相关的样式。所有的style元素和link元素引入的css都保存在这里。

那么,它是怎么做到呢? 它来自于Document::styleResolverChanged函数。这个函数被很多地方调用,当CSS改变的时候,或者发生页面大小变化等的时候,该函数会被触发。

这个调用序列是:

Document::styleResolverChanged -> DocumentStyleSheetCollection::updateActiveStyleSheets -> StyleResolver::appendAuthorStyleSheets


至此,CSS的解析的大略已经基本完成了。


原创粉丝点击