本章的目的,主要說明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 }
注意函數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的解析的大略已經基本完成了。