本章的目的,主要说明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 }
注意函数collectMatchRules和sortAndTransferMatchedRules。这两个函数,一个是收集选装的规则,一个是对规则进行排序。
那么,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的解析的大略已经基本完成了。
本文探讨了WebKit内核中CSS如何与Render树结合,重点关注RenderStyle和StyleResolver。StyleResolver从CSS中生成RenderStyle,并通过styleForElement进行元素的选择和匹配。RuleSet代表CSS规则,其来源包括默认和作者定义。Element的RuleSet来源于内联CSS,而AuthorStyle来源于style元素和link元素引入的CSS。当CSS或页面状态改变时,Document会触发更新流程。
4477

被折叠的 条评论
为什么被折叠?



