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

本文探讨了WebKit内核中CSS如何与Render树结合,重点关注RenderStyle和StyleResolver。StyleResolver从CSS中生成RenderStyle,并通过styleForElement进行元素的选择和匹配。RuleSet代表CSS规则,其来源包括默认和作者定义。Element的RuleSet来源于内联CSS,而AuthorStyle来源于style元素和link元素引入的CSS。当CSS或页面状态改变时,Document会触发更新流程。

本章的目的,主要说明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     else
1574         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的解析的大略已经基本完成了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值