webkit核心原始碼導讀5: CSS如何作用到Render樹上

來源:互聯網
上載者:User

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

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.