Changeset 104060 in webkit


Ignore:
Timestamp:
Jan 4, 2012 12:03:35 PM (12 years ago)
Author:
Antti Koivisto
Message:

Analyze stylesheet scope to minimize style recalcs
https://bugs.webkit.org/show_bug.cgi?id=75508

Reviewed by Dave Hyatt.

It is a relatively common pattern to use inline stylesheets in document body where all rules are scoped using descendant selector

<style>
#foo {...}
#foo div {...}
#foo .bar {...}
</style>

When this pattern is used it is also common that the rules only apply to elements that come after the style element.

When the set of active stylesheets changes we invalidate and recompute the entire document style. This is very expensive. We can
detect the case above and avoid the style recalc.

On engadget.com, this patch cuts the time spent in style recalcs to roughly half. There are further savings from reduced
relayouts. In total the engine CPU time used over the page load is reduced by ~10%.

  • css/CSSStyleSelector.cpp:

(WebCore::CSSStyleSelector::CSSStyleSelector):
(WebCore::CSSStyleSelector::collectFeatures):

Refactor feature collection from constructor to a separate function.


(WebCore::CSSStyleSelector::appendAuthorStylesheets):

New function for non-destructively updating the style selector.


(WebCore::CSSStyleSelector::Features::clear):

Clear the features for another collection.


(WebCore::CSSStyleSelector::determineStylesheetSelectorScopes):

Find if all rules on a stylesheetare scoped to some set of ids and classes.


(WebCore::CSSStyleSelector::styleForDocument):

Add optional font selector argument. We updated the correct base style font on style selector construction but that is no longer sufficient
as font selector may be updated without reconstructing the style selector.


(WebCore::RuleSet::addRulesFromSheet):

Invalidate the matched declaration cache in case of new font-face rules.


  • css/CSSStyleSelector.h:
  • css/SelectorChecker.cpp:

(WebCore::SelectorChecker::determineSelectorScopes):

Find if all rules on a selector list are scoped to some set of ids and classes.


  • css/SelectorChecker.h:
  • dom/Document.cpp:

(WebCore::Document::Document):
(WebCore::Document::recalcStyle):

Pass the font selector, if exists, to styleForDocument so we always have the base font up to date.


(WebCore::Document::combineCSSFeatureFlags):
(WebCore::Document::resetCSSFeatureFlags):
(WebCore::Document::createStyleSelector):

Refactor css feature flag resetting to functions.


(WebCore::Document::removePendingSheet):

Use new PendingStylesheetCompleted flag when new stylesheets arrive.

(WebCore::Document::styleSelectorChanged):

Skip style recalc if it is not needed.

(WebCore::Document::collectActiveStylesheets):

Refactor collecting stylesheets to a separate function.


(WebCore::Document::testAddedStylesheetRequiresStyleRecalc):

Determine the scopes and use hasElementWithId/getElementsByClassName to figure out if any scoped elements currently exist in the tree.


(WebCore::Document::analyzeStylesheetChange):

Figure out if we can update the style selector incrementally and if we can skip the style recalc.


(WebCore::Document::updateActiveStylesheets):

Renamed from recalcStyleSelector.
Invoke the new analysis functions.


  • dom/Document.h:
Location:
trunk/Source/WebCore
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r104047 r104060  
     12012-01-03  Antti Koivisto  <antti@apple.com>
     2
     3        Reviewed by Dave Hyatt.
     4
     5        Analyze stylesheet scope to minimize style recalcs
     6        https://bugs.webkit.org/show_bug.cgi?id=75508
     7       
     8        It is a relatively common pattern to use inline stylesheets in document body where all rules are scoped using descendant selector
     9
     10        <style>
     11        #foo {...}
     12        #foo div {...}
     13        #foo .bar {...}
     14        </style>
     15
     16        When this pattern is used it is also common that the rules only apply to elements that come after the style element.
     17
     18        When the set of active stylesheets changes we invalidate and recompute the entire document style. This is very expensive. We can
     19        detect the case above and avoid the style recalc.
     20       
     21        On engadget.com, this patch cuts the time spent in style recalcs to roughly half. There are further savings from reduced
     22        relayouts. In total the engine CPU time used over the page load is reduced by ~10%.
     23
     24        * css/CSSStyleSelector.cpp:
     25        (WebCore::CSSStyleSelector::CSSStyleSelector):
     26        (WebCore::CSSStyleSelector::collectFeatures):
     27       
     28            Refactor feature collection from constructor to a separate function.
     29       
     30        (WebCore::CSSStyleSelector::appendAuthorStylesheets):
     31       
     32            New function for non-destructively updating the style selector.
     33       
     34        (WebCore::CSSStyleSelector::Features::clear):
     35       
     36            Clear the features for another collection.
     37           
     38        (WebCore::CSSStyleSelector::determineStylesheetSelectorScopes):
     39       
     40            Find if all rules on a stylesheetare scoped to some set of ids and classes.
     41       
     42        (WebCore::CSSStyleSelector::styleForDocument):
     43       
     44            Add optional font selector argument. We updated the correct base style font on style selector construction but that is no longer sufficient
     45            as font selector may be updated without reconstructing the style selector.
     46       
     47        (WebCore::RuleSet::addRulesFromSheet):
     48       
     49            Invalidate the matched declaration cache in case of new font-face rules.
     50       
     51        * css/CSSStyleSelector.h:
     52        * css/SelectorChecker.cpp:
     53        (WebCore::SelectorChecker::determineSelectorScopes):
     54       
     55            Find if all rules on a selector list are scoped to some set of ids and classes.
     56       
     57        * css/SelectorChecker.h:
     58        * dom/Document.cpp:
     59        (WebCore::Document::Document):
     60        (WebCore::Document::recalcStyle):
     61       
     62            Pass the font selector, if exists, to styleForDocument so we always have the base font up to date.
     63       
     64        (WebCore::Document::combineCSSFeatureFlags):
     65        (WebCore::Document::resetCSSFeatureFlags):
     66        (WebCore::Document::createStyleSelector):
     67
     68            Refactor css feature flag resetting to functions.
     69       
     70        (WebCore::Document::removePendingSheet):
     71       
     72            Use new PendingStylesheetCompleted flag when new stylesheets arrive.
     73
     74        (WebCore::Document::styleSelectorChanged):
     75
     76            Skip style recalc if it is not needed.
     77
     78        (WebCore::Document::collectActiveStylesheets):
     79       
     80            Refactor collecting stylesheets to a separate function.
     81       
     82        (WebCore::Document::testAddedStylesheetRequiresStyleRecalc):
     83       
     84            Determine the scopes and use hasElementWithId/getElementsByClassName to figure out if any scoped elements currently exist in the tree.
     85       
     86        (WebCore::Document::analyzeStylesheetChange):
     87       
     88            Figure out if we can update the style selector incrementally and if we can skip the style recalc.
     89       
     90        (WebCore::Document::updateActiveStylesheets):
     91       
     92            Renamed from recalcStyleSelector.
     93            Invoke the new analysis functions.
     94       
     95        * dom/Document.h:
     96
    1972012-01-04  Igor Oliveira  <igor.oliveira@openbossa.org>
    298
  • trunk/Source/WebCore/css/CSSStyleSelector.cpp

    r104036 r104060  
    419419            m_authorStyle->addRulesFromSheet(static_cast<CSSStyleSheet*>(sheet), *m_medium, this);
    420420    }
     421    m_authorStyle->shrinkToFit();
     422
     423    collectFeatures();
     424   
     425    if (document->renderer() && document->renderer()->style())
     426        document->renderer()->style()->font().update(fontSelector());
     427}
     428
     429void CSSStyleSelector::collectFeatures()
     430{
    421431    // Collect all ids and rules using sibling selectors (:first-child and similar)
    422432    // in the current set of stylesheets. Style sharing code uses this information to reject
     
    430440    if (m_userStyle)
    431441        m_userStyle->collectFeatures(m_features);
    432 
    433     m_authorStyle->shrinkToFit();
    434442    if (m_features.siblingRules)
    435443        m_features.siblingRules->shrinkToFit();
    436444    if (m_features.uncommonAttributeRules)
    437445        m_features.uncommonAttributeRules->shrinkToFit();
    438 
    439     if (document->renderer() && document->renderer()->style())
    440         document->renderer()->style()->font().update(fontSelector());
     446}
     447
     448void CSSStyleSelector::appendAuthorStylesheets(unsigned firstNew, const Vector<RefPtr<StyleSheet> >& stylesheets)
     449{
     450    // This handles sheets added to the end of the stylesheet list only. In other cases the style resolver
     451    // needs to be reconstructed. To handle insertions too the rule order numbers would need to be updated.
     452    unsigned size = stylesheets.size();
     453    for (unsigned i = firstNew; i < size; ++i) {
     454        if (!stylesheets[i]->isCSSStyleSheet() || stylesheets[i]->disabled())
     455            continue;
     456        m_authorStyle->addRulesFromSheet(static_cast<CSSStyleSheet*>(stylesheets[i].get()), *m_medium, this);
     457    }
     458    m_authorStyle->shrinkToFit();
     459    // FIXME: This really only needs to collect the features from the newly added sheets.
     460    m_features.clear();
     461    collectFeatures();
     462   
     463    if (document()->renderer() && document()->renderer()->style())
     464        document()->renderer()->style()->font().update(fontSelector());
    441465}
    442466
     
    468492CSSStyleSelector::Features::~Features()
    469493{
     494}
     495
     496void CSSStyleSelector::Features::clear()
     497{
     498    idsInRules.clear();
     499    attrsInRules.clear();
     500    siblingRules.clear();
     501    uncommonAttributeRules.clear();
     502    usesFirstLineRules = false;
     503    usesBeforeAfterRules = false;
     504    usesLinkRules = false;
    470505}
    471506
     
    11491184}
    11501185
    1151 PassRefPtr<RenderStyle> CSSStyleSelector::styleForDocument(Document* document)
     1186PassRefPtr<RenderStyle> CSSStyleSelector::styleForDocument(Document* document, CSSFontSelector* fontSelector)
    11521187{
    11531188    Frame* frame = document->frame();
     
    12051240
    12061241    documentStyle->setFontDescription(fontDescription);
    1207     documentStyle->font().update(0);
     1242    documentStyle->font().update(fontSelector);
    12081243
    12091244    return documentStyle.release();
     
    18001835    return true;
    18011836}
     1837   
     1838bool CSSStyleSelector::determineStylesheetSelectorScopes(CSSStyleSheet* stylesheet, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes)
     1839{
     1840    ASSERT(!stylesheet->isLoading());
     1841   
     1842    size_t size = stylesheet->length();
     1843    for (size_t i = 0; i < size; i++) {
     1844        CSSRule* rule = stylesheet->item(i);
     1845        if (rule->isStyleRule()) {
     1846            CSSStyleRule* styleRule = static_cast<CSSStyleRule*>(rule);
     1847            if (!SelectorChecker::determineSelectorScopes(styleRule->selectorList(), idScopes, classScopes))
     1848                return false;
     1849            continue;
     1850        }
     1851        if (rule->isImportRule()) {
     1852            CSSImportRule* importRule = static_cast<CSSImportRule*>(rule);
     1853            if (importRule->styleSheet()) {
     1854                if (!determineStylesheetSelectorScopes(importRule->styleSheet(), idScopes, classScopes))
     1855                    return false;
     1856            }
     1857            continue;
     1858        }
     1859        // FIXME: Media rules and maybe some others could be allowed.
     1860        return false;
     1861    }
     1862    return true;
     1863}
    18021864
    18031865// -----------------------------------------------------------------
     
    19692031                        const CSSFontFaceRule* fontFaceRule = static_cast<CSSFontFaceRule*>(childItem);
    19702032                        styleSelector->fontSelector()->addFontFaceRule(fontFaceRule);
     2033                        styleSelector->invalidateMatchedDeclarationCache();
    19712034                    } else if (childItem->isKeyframesRule() && styleSelector) {
    19722035                        // Add this keyframe rule to our set.
     
    19792042            const CSSFontFaceRule* fontFaceRule = static_cast<CSSFontFaceRule*>(rule);
    19802043            styleSelector->fontSelector()->addFontFaceRule(fontFaceRule);
     2044            styleSelector->invalidateMatchedDeclarationCache();
    19812045        } else if (rule->isKeyframesRule())
    19822046            styleSelector->addKeyframeStyle(static_cast<WebKitCSSKeyframesRule*>(rule));
  • trunk/Source/WebCore/css/CSSStyleSelector.h

    r104036 r104060  
    113113    PassRefPtr<RenderStyle> styleForPage(int pageIndex);
    114114
    115     static PassRefPtr<RenderStyle> styleForDocument(Document*);
     115    static PassRefPtr<RenderStyle> styleForDocument(Document*, CSSFontSelector* = 0);
    116116
    117117    RenderStyle* style() const { return m_style.get(); }
     
    127127    void setTextSizeAdjust(bool b) { m_fontDirty |= style()->setTextSizeAdjust(b); }
    128128    bool hasParentNode() const { return m_parentNode; }
     129   
     130    void appendAuthorStylesheets(unsigned firstNew, const Vector<RefPtr<StyleSheet> >&);
     131   
     132    // Find the ids or classes the selectors on a stylesheet are scoped to. The selectors only apply to elements in subtrees where the root element matches the scope.
     133    static bool determineStylesheetSelectorScopes(CSSStyleSheet*, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes);
    129134
    130135private:
    131136    void initForStyleResolve(Element*, RenderStyle* parentStyle = 0, PseudoId = NOPSEUDO);
    132137    void initElement(Element*);
     138    void collectFeatures();
    133139    RenderStyle* locateSharedStyle();
    134140    bool matchesRuleSet(RuleSet*);
     
    219225        Features();
    220226        ~Features();
     227        void clear();
    221228        HashSet<AtomicStringImpl*> idsInRules;
    222229        HashSet<AtomicStringImpl*> attrsInRules;
  • trunk/Source/WebCore/css/SelectorChecker.cpp

    r102770 r104060  
    13991399}
    14001400
    1401 }
     1401bool SelectorChecker::determineSelectorScopes(const CSSSelectorList& selectorList, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes)
     1402{
     1403    for (CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) {
     1404        CSSSelector* rightmostSelector = selector;
     1405        CSSSelector::Relation relation = CSSSelector::Descendant;
     1406        for (;rightmostSelector->tagHistory(); rightmostSelector = rightmostSelector->tagHistory()) {
     1407            if (rightmostSelector->relation() != CSSSelector::SubSelector)
     1408                relation = rightmostSelector->relation();
     1409        }
     1410        if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
     1411            return false;
     1412        if (rightmostSelector->m_match == CSSSelector::Id) {
     1413            idScopes.add(rightmostSelector->value().impl());
     1414            continue;
     1415        }
     1416        if (rightmostSelector->m_match == CSSSelector::Class) {
     1417            classScopes.add(rightmostSelector->value().impl());
     1418            continue;
     1419        }
     1420        return false;
     1421    }
     1422    return true;
     1423}
     1424
     1425}
  • trunk/Source/WebCore/css/SelectorChecker.h

    r102770 r104060  
    9191    static unsigned determineLinkMatchType(const CSSSelector*);
    9292
     93    // Find the ids or classes selectors are scoped to. The selectors only apply to elements in subtrees where the root element matches the scope.
     94    static bool determineSelectorScopes(const CSSSelectorList&, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes);
     95
    9396private:
    9497    bool checkOneSelector(CSSSelector*, Element*, PseudoId& dynamicPseudo, bool isSubSelector, VisitedMatchType, RenderStyle*, RenderStyle* elementParentStyle) const;
  • trunk/Source/WebCore/dom/Document.cpp

    r103883 r104060  
    383383#endif
    384384    , m_styleSheets(StyleSheetList::create(this))
     385    , m_hadActiveLoadingStylesheet(false)
    385386    , m_readyState(Complete)
    386387    , m_styleRecalcTimer(this, &Document::styleRecalcTimerFired)
     
    15291530   
    15301531    if (m_hasDirtyStyleSelector)
    1531         recalcStyleSelector();
     1532        updateActiveStylesheets(RecalcStyleImmediately);
    15321533
    15331534    InspectorInstrumentationCookie cookie = InspectorInstrumentation::willRecalculateStyle(this);
     
    15541555        m_hasNodesWithPlaceholderStyle = false;
    15551556       
    1556         RefPtr<RenderStyle> documentStyle = CSSStyleSelector::styleForDocument(this);
     1557        RefPtr<RenderStyle> documentStyle = CSSStyleSelector::styleForDocument(this, m_styleSelector ? m_styleSelector->fontSelector() : 0);
    15571558        StyleChange ch = diff(documentStyle.get(), renderer()->style());
    15581559        if (ch != NoChange)
     
    15851586   
    15861587    // Pseudo element removal and similar may only work with these flags still set. Reset them after the style recalc.
    1587     if (m_styleSelector) {
    1588         m_usesSiblingRules = m_styleSelector->usesSiblingRules();
    1589         m_usesFirstLineRules = m_styleSelector->usesFirstLineRules();
    1590         m_usesBeforeAfterRules = m_styleSelector->usesBeforeAfterRules();
    1591         m_usesLinkRules = m_styleSelector->usesLinkRules();
    1592     }
     1588    if (m_styleSelector)
     1589        resetCSSFeatureFlags();
    15931590
    15941591    if (frameView) {
     
    17791776}
    17801777
     1778void Document::combineCSSFeatureFlags()
     1779{
     1780    // Delay resetting the flags until after next style recalc since unapplying the style may not work without these set (this is true at least with before/after).
     1781    m_usesSiblingRules = m_usesSiblingRules || m_styleSelector->usesSiblingRules();
     1782    m_usesFirstLineRules = m_usesFirstLineRules || m_styleSelector->usesFirstLineRules();
     1783    m_usesBeforeAfterRules = m_usesBeforeAfterRules || m_styleSelector->usesBeforeAfterRules();
     1784    m_usesLinkRules = m_usesLinkRules || m_styleSelector->usesLinkRules();
     1785}
     1786
     1787void Document::resetCSSFeatureFlags()
     1788{
     1789    m_usesSiblingRules = m_styleSelector->usesSiblingRules();
     1790    m_usesFirstLineRules = m_styleSelector->usesFirstLineRules();
     1791    m_usesBeforeAfterRules = m_styleSelector->usesBeforeAfterRules();
     1792    m_usesLinkRules = m_styleSelector->usesLinkRules();
     1793}
     1794
    17811795void Document::createStyleSelector()
    17821796{
     
    17861800    m_styleSelector = adoptPtr(new CSSStyleSelector(this, m_styleSheets.get(), m_mappedElementSheet.get(), pageUserSheet(), pageGroupUserSheets(), m_userSheets.get(),
    17871801                                                    !inQuirksMode(), matchAuthorAndUserStyles));
    1788     // Delay resetting the flags until after next style recalc since unapplying the style may not work without these set (this is true at least with before/after).
    1789     m_usesSiblingRules = m_usesSiblingRules || m_styleSelector->usesSiblingRules();
    1790     m_usesFirstLineRules = m_usesFirstLineRules || m_styleSelector->usesFirstLineRules();
    1791     m_usesBeforeAfterRules = m_usesBeforeAfterRules || m_styleSelector->usesBeforeAfterRules();
    1792     m_usesLinkRules = m_usesLinkRules || m_styleSelector->usesLinkRules();
     1802    combineCSSFeatureFlags();
    17931803}
    17941804
     
    29512961        return;
    29522962
    2953     styleSelectorChanged(RecalcStyleImmediately);
     2963    styleSelectorChanged(RecalcStyleIfNeeded);
    29542964
    29552965    if (ScriptableDocumentParser* parser = scriptableDocumentParser())
     
    29722982#endif
    29732983
    2974     recalcStyleSelector();
    2975    
     2984    bool stylesheetChangeRequiresStyleRecalc = updateActiveStylesheets(updateFlag);
     2985    if (!stylesheetChangeRequiresStyleRecalc)
     2986        return;
     2987
    29762988    if (updateFlag == DeferRecalcStyle) {
    29772989        scheduleForcedStyleRecalc();
     
    30453057    m_styleSheetCandidateNodes.remove(node);
    30463058}
    3047 
    3048 void Document::recalcStyleSelector()
    3049 {
    3050     if (m_inStyleRecalc) {
    3051         // SVG <use> element may manage to invalidate style selector in the middle of a style recalc.
    3052         // https://bugs.webkit.org/show_bug.cgi?id=54344
    3053         // FIXME: This should be fixed in SVG and this code replaced with ASSERT(!m_inStyleRecalc).
    3054         m_hasDirtyStyleSelector = true;
    3055         scheduleForcedStyleRecalc();
    3056         return;
    3057     }
    3058     if (!renderer() || !attached())
    3059         return;
    3060 
    3061     StyleSheetVector sheets;
    3062 
     3059   
     3060void Document::collectActiveStylesheets(Vector<RefPtr<StyleSheet> >& sheets)
     3061{
    30633062    bool matchAuthorAndUserStyles = true;
    30643063    if (Settings* settings = this->settings())
     
    30713070    for (StyleSheetCandidateListHashSet::iterator it = begin; it != end; ++it) {
    30723071        Node* n = *it;
    3073 
    30743072        StyleSheet* sheet = 0;
    3075 
    30763073        if (n->nodeType() == PROCESSING_INSTRUCTION_NODE) {
    30773074            // Processing instruction (XML documents only).
     
    30903087        } else if ((n->isHTMLElement() && (n->hasTagName(linkTag) || n->hasTagName(styleTag)))
    30913088#if ENABLE(SVG)
    3092             ||  (n->isSVGElement() && n->hasTagName(SVGNames::styleTag))
    3093 #endif
    3094         ) {
     3089                   ||  (n->isSVGElement() && n->hasTagName(SVGNames::styleTag))
     3090#endif
     3091                   ) {
    30953092            Element* e = static_cast<Element*>(n);
    30963093            AtomicString title = e->getAttribute(titleAttr);
     
    31163113                    title = nullAtom;
    31173114            }
    3118 
    31193115            // Get the current preferred styleset.  This is the
    31203116            // set of sheets that will be enabled.
     
    31293125                // <STYLE> element
    31303126                sheet = static_cast<HTMLStyleElement*>(n)->sheet();
    3131 
    31323127            // Check to see if this sheet belongs to a styleset
    31333128            // (thus making it PREFERRED or ALTERNATE rather than
     
    31443139                        m_preferredStylesheetSet = m_selectedStylesheetSet = title;
    31453140                }
    3146 
    31473141                if (title != m_preferredStylesheetSet)
    31483142                    sheet = 0;
    31493143            }
    31503144        }
    3151 
    31523145        if (sheet)
    31533146            sheets.append(sheet);
    31543147    }
    3155 
    3156     m_styleSheets->swap(sheets);
    3157 
    3158     m_styleSelector.clear();
     3148}
     3149
     3150bool Document::testAddedStylesheetRequiresStyleRecalc(CSSStyleSheet* stylesheet)
     3151{
     3152    if (stylesheet->disabled())
     3153        return false;
     3154    // See if all rules on the sheet are scoped to some specific ids or classes.
     3155    // Then test if we actually have any of those in the tree at the moment.
     3156    // FIXME: Even we if we find some, we could just invalidate those subtrees instead of invalidating the entire style.
     3157    HashSet<AtomicStringImpl*> idScopes;
     3158    HashSet<AtomicStringImpl*> classScopes;
     3159    if (!CSSStyleSelector::determineStylesheetSelectorScopes(stylesheet, idScopes, classScopes))
     3160        return true;
     3161    // Testing for classes is not particularly fast so bail out if there are more than a few.
     3162    static const int maximumClassScopesToTest = 4;
     3163    if (classScopes.size() > maximumClassScopesToTest)
     3164        return true;
     3165    HashSet<AtomicStringImpl*>::iterator end = idScopes.end();
     3166    for (HashSet<AtomicStringImpl*>::iterator it = idScopes.begin(); it != end; ++it) {
     3167        if (hasElementWithId(*it))
     3168            return true;
     3169    }
     3170    end = classScopes.end();
     3171    for (HashSet<AtomicStringImpl*>::iterator it = classScopes.begin(); it != end; ++it) {
     3172        // FIXME: getElementsByClassName is not optimal for this. We should handle all classes in a single pass.
     3173        if (getElementsByClassName(*it)->length())
     3174            return true;
     3175    }
     3176    return false;
     3177}
     3178   
     3179void Document::analyzeStylesheetChange(StyleSelectorUpdateFlag updateFlag, const Vector<RefPtr<StyleSheet> >& newStylesheets, bool& requiresStyleSelectorReset, bool& requiresStyleRecalc)
     3180{
     3181    requiresStyleSelectorReset = true;
     3182    requiresStyleRecalc = true;
     3183   
     3184    // Stylesheets of <style> elements that @import stylesheets are active but loading. We need to trigger a full recalc when such loads are done.
     3185    bool hasActiveLoadingStylesheet = false;
     3186    unsigned newStylesheetCount = newStylesheets.size();
     3187    for (unsigned i = 0; i < newStylesheetCount; ++i) {
     3188        if (newStylesheets[i]->isLoading())
     3189            hasActiveLoadingStylesheet = true;
     3190    }
     3191    if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) {
     3192        m_hadActiveLoadingStylesheet = false;
     3193        return;
     3194    }
     3195    m_hadActiveLoadingStylesheet = hasActiveLoadingStylesheet;
     3196
     3197    if (updateFlag != RecalcStyleIfNeeded)
     3198        return;
     3199    if (!m_styleSelector)
     3200        return;
     3201
     3202    // See if we are just adding stylesheets.
     3203    unsigned oldStylesheetCount = m_styleSheets->length();
     3204    if (newStylesheetCount < oldStylesheetCount)
     3205        return;
     3206    for (unsigned i = 0; i < oldStylesheetCount; ++i) {
     3207        if (m_styleSheets->item(i) != newStylesheets[i])
     3208            return;
     3209    }
     3210    requiresStyleSelectorReset = false;
     3211
     3212    // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs.
     3213    if (!body() || m_hasNodesWithPlaceholderStyle)
     3214        return;
     3215    for (unsigned i = oldStylesheetCount; i < newStylesheetCount; ++i) {
     3216        if (!newStylesheets[i]->isCSSStyleSheet())
     3217            return;
     3218        if (testAddedStylesheetRequiresStyleRecalc(static_cast<CSSStyleSheet*>(newStylesheets[i].get())))
     3219            return;
     3220    }
     3221    requiresStyleRecalc = false;
     3222}
     3223
     3224bool Document::updateActiveStylesheets(StyleSelectorUpdateFlag updateFlag)
     3225{
     3226    if (m_inStyleRecalc) {
     3227        // SVG <use> element may manage to invalidate style selector in the middle of a style recalc.
     3228        // https://bugs.webkit.org/show_bug.cgi?id=54344
     3229        // FIXME: This should be fixed in SVG and this code replaced with ASSERT(!m_inStyleRecalc).
     3230        m_hasDirtyStyleSelector = true;
     3231        scheduleForcedStyleRecalc();
     3232        return false;
     3233    }
     3234    if (!renderer() || !attached())
     3235        return false;
     3236
     3237    StyleSheetVector newStylesheets;
     3238    collectActiveStylesheets(newStylesheets);
     3239
     3240    bool requiresStyleSelectorReset;
     3241    bool requiresStyleRecalc;
     3242    analyzeStylesheetChange(updateFlag, newStylesheets, requiresStyleSelectorReset, requiresStyleRecalc);
     3243
     3244    if (requiresStyleSelectorReset)
     3245        m_styleSelector.clear();
     3246    else {
     3247        m_styleSelector->appendAuthorStylesheets(m_styleSheets->length(), newStylesheets);
     3248        resetCSSFeatureFlags();
     3249    }
     3250    m_styleSheets->swap(newStylesheets);
     3251
    31593252    m_didCalculateStyleSelector = true;
    31603253    m_hasDirtyStyleSelector = false;
     3254   
     3255    return requiresStyleRecalc;
    31613256}
    31623257
  • trunk/Source/WebCore/dom/Document.h

    r103865 r104060  
    212212};
    213213
    214 enum StyleSelectorUpdateFlag { RecalcStyleImmediately, DeferRecalcStyle };
     214enum StyleSelectorUpdateFlag { RecalcStyleImmediately, DeferRecalcStyle, RecalcStyleIfNeeded };
    215215
    216216class Document : public TreeScope, public ScriptExecutionContext {
     
    457457
    458458    /**
    459      * Updates the pending sheet count and then calls updateStyleSelector.
     459     * Updates the pending sheet count and then calls updateActiveStylesheets.
    460460     */
    461461    void removePendingSheet();
     
    492492     */
    493493    void styleSelectorChanged(StyleSelectorUpdateFlag);
    494     void recalcStyleSelector();
    495494
    496495    bool usesSiblingRules() const { return m_usesSiblingRules || m_usesSiblingRulesOverride; }
     
    11621161
    11631162    void createStyleSelector();
     1163    void combineCSSFeatureFlags();
     1164    void resetCSSFeatureFlags();
     1165   
     1166    bool updateActiveStylesheets(StyleSelectorUpdateFlag);
     1167    void collectActiveStylesheets(Vector<RefPtr<StyleSheet> >&);
     1168    bool testAddedStylesheetRequiresStyleRecalc(CSSStyleSheet*);
     1169    void analyzeStylesheetChange(StyleSelectorUpdateFlag, const Vector<RefPtr<StyleSheet> >& newStylesheets, bool& requiresStyleSelectorReset, bool& requiresStyleRecalc);
    11641170
    11651171    void deleteCustomFonts();
     
    12651271
    12661272    RefPtr<StyleSheetList> m_styleSheets; // All of the stylesheets that are currently in effect for our media type and stylesheet set.
     1273    bool m_hadActiveLoadingStylesheet;
    12671274   
    12681275    typedef ListHashSet<Node*, 32> StyleSheetCandidateListHashSet;
Note: See TracChangeset for help on using the changeset viewer.