Changeset 163592 in webkit


Ignore:
Timestamp:
Feb 6, 2014 5:58:40 PM (10 years ago)
Author:
Antti Koivisto
Message:

Check selectors exactly when invalidating style
https://bugs.webkit.org/show_bug.cgi?id=128321

Reviewed by Andreas Kling.

Selectors are now really fast to match with the JIT. Take advantage of this by invalidating
the document style exactly when a new stylesheet arrives (instead of using heuristics).

This reduces need for large style recalculations in some common cases.

  • css/ElementRuleCollector.cpp:

(WebCore::ElementRuleCollector::clearMatchedRules):
(WebCore::ElementRuleCollector::collectMatchingRulesForList):

  • css/ElementRuleCollector.h:

(WebCore::ElementRuleCollector::hasMatchedRules):

  • css/RuleSet.h:

(WebCore::RuleSet::hasShadowPseudoElementRules):

  • css/SelectorChecker.cpp:

(WebCore::SelectorChecker::matchRecursively):

  • css/SelectorChecker.h:


Add new mode where all pseudo elements match so we can invalidate their element.

  • css/StyleInvalidationAnalysis.cpp:

(WebCore::shouldDirtyAllStyle):
(WebCore::StyleInvalidationAnalysis::StyleInvalidationAnalysis):
(WebCore::invalidateStyleRecursively):
(WebCore::StyleInvalidationAnalysis::invalidateStyle):

Switch to real selector checker.

  • css/StyleInvalidationAnalysis.h:
  • css/StyleResolver.cpp:

(WebCore::StyleResolver::MatchedProperties::~MatchedProperties):

  • css/StyleResolver.h:

(WebCore::StyleResolver::mediaQueryEvaluator):

  • dom/DocumentStyleSheetCollection.cpp:

(WebCore::DocumentStyleSheetCollection::analyzeStyleSheetChange):

Location:
trunk/Source/WebCore
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r163591 r163592  
     12014-02-06  Antti Koivisto  <antti@apple.com>
     2
     3        Check selectors exactly when invalidating style
     4        https://bugs.webkit.org/show_bug.cgi?id=128321
     5
     6        Reviewed by Andreas Kling.
     7       
     8        Selectors are now really fast to match with the JIT. Take advantage of this by invalidating
     9        the document style exactly when a new stylesheet arrives (instead of using heuristics).
     10       
     11        This reduces need for large style recalculations in some common cases.
     12
     13        * css/ElementRuleCollector.cpp:
     14        (WebCore::ElementRuleCollector::clearMatchedRules):
     15        (WebCore::ElementRuleCollector::collectMatchingRulesForList):
     16        * css/ElementRuleCollector.h:
     17        (WebCore::ElementRuleCollector::hasMatchedRules):
     18        * css/RuleSet.h:
     19        (WebCore::RuleSet::hasShadowPseudoElementRules):
     20        * css/SelectorChecker.cpp:
     21        (WebCore::SelectorChecker::matchRecursively):
     22        * css/SelectorChecker.h:
     23       
     24            Add new mode where all pseudo elements match so we can invalidate their element.
     25
     26        * css/StyleInvalidationAnalysis.cpp:
     27        (WebCore::shouldDirtyAllStyle):
     28        (WebCore::StyleInvalidationAnalysis::StyleInvalidationAnalysis):
     29        (WebCore::invalidateStyleRecursively):
     30        (WebCore::StyleInvalidationAnalysis::invalidateStyle):
     31       
     32            Switch to real selector checker.
     33
     34        * css/StyleInvalidationAnalysis.h:
     35        * css/StyleResolver.cpp:
     36        (WebCore::StyleResolver::MatchedProperties::~MatchedProperties):
     37        * css/StyleResolver.h:
     38        (WebCore::StyleResolver::mediaQueryEvaluator):
     39        * dom/DocumentStyleSheetCollection.cpp:
     40        (WebCore::DocumentStyleSheetCollection::analyzeStyleSheetChange):
     41
    1422014-02-06  Gavin Barraclough  <barraclough@apple.com>
    243
  • trunk/Source/WebCore/css/ElementRuleCollector.cpp

    r163559 r163592  
    9595}
    9696
    97 inline void ElementRuleCollector::clearMatchedRules()
     97void ElementRuleCollector::clearMatchedRules()
    9898{
    9999    if (!m_matchedRules)
     
    360360        if (ruleMatches(ruleData, dynamicPseudo)) {
    361361            // For SharingRules testing, any match is good enough, we don't care what is matched.
    362             if (m_mode == SelectorChecker::SharingRules) {
     362            if (m_mode == SelectorChecker::SharingRules || m_mode == SelectorChecker::StyleInvalidation) {
    363363                addMatchedRule(&ruleData);
    364364                break;
  • trunk/Source/WebCore/css/ElementRuleCollector.h

    r163475 r163592  
    7171    const Vector<RefPtr<StyleRuleBase>>& matchedRuleList() const;
    7272
     73    bool hasMatchedRules() const { return m_matchedRules && !m_matchedRules->isEmpty(); }
     74    void clearMatchedRules();
     75
    7376private:
    7477    void addElementStyleProperties(const StyleProperties*, bool isCacheable = true);
     
    8588
    8689    void addMatchedRule(const RuleData*);
    87     void clearMatchedRules();
    8890
    8991    Element& m_element;
  • trunk/Source/WebCore/css/RuleSet.h

    r163559 r163592  
    167167    unsigned ruleCount() const { return m_ruleCount; }
    168168
     169    bool hasShadowPseudoElementRules() const { return !m_shadowPseudoElementRules.isEmpty(); }
     170
    169171private:
    170172    void addChildRules(const Vector<RefPtr<StyleRuleBase>>&, const MediaQueryEvaluator& medium, StyleResolver*, bool hasDocumentSecurityOrigin, AddRuleFlags);
  • trunk/Source/WebCore/css/SelectorChecker.cpp

    r163427 r163592  
    33 *           (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
    44 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
    5  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
     5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Apple Inc. All rights reserved.
    66 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
    77 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
     
    163163                if (context.selector->pseudoType() == CSSSelector::PseudoWebKitCustomElement && root->type() != ShadowRoot::UserAgentShadowRoot)
    164164                    return SelectorFailsLocally;
    165             } else
     165            } else if (m_mode != StyleInvalidation)
    166166                return SelectorFailsLocally;
    167167        } else {
     
    169169                return SelectorFailsLocally;
    170170
    171             PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoType());
     171            // When invalidating style all pseudo elements need to match.
     172            PseudoId pseudoId = m_mode == StyleInvalidation ? NOPSEUDO : CSSSelector::pseudoId(context.selector->pseudoType());
    172173            if (pseudoId == FIRST_LETTER)
    173174                context.element->document().styleSheetCollection().setUsesFirstLetterRules(true);
  • trunk/Source/WebCore/css/SelectorChecker.h

    r162394 r163592  
    4848public:
    4949    enum VisitedMatchType { VisitedMatchDisabled, VisitedMatchEnabled };
    50     enum Mode { ResolvingStyle = 0, CollectingRules, QueryingRules, SharingRules };
     50    enum Mode { ResolvingStyle = 0, CollectingRules, QueryingRules, SharingRules, StyleInvalidation };
    5151
    5252    SelectorChecker(Document&, Mode);
  • trunk/Source/WebCore/css/StyleInvalidationAnalysis.cpp

    r161196 r163592  
    11/*
    2  * Copyright (C) 2012 Apple Inc. All rights reserved.
     2 * Copyright (C) 2012, 2014 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    3030#include "Document.h"
    3131#include "ElementIterator.h"
     32#include "ElementRuleCollector.h"
     33#include "SelectorFilter.h"
    3234#include "StyleRuleImport.h"
    3335#include "StyleSheetContents.h"
    34 #include "StyledElement.h"
    3536
    3637namespace WebCore {
    3738
    38 StyleInvalidationAnalysis::StyleInvalidationAnalysis(const Vector<StyleSheetContents*>& sheets)
    39     : m_dirtiesAllStyle(false)
     39static bool shouldDirtyAllStyle(const Vector<RefPtr<StyleRuleBase>> rules)
    4040{
    41     for (unsigned i = 0; i < sheets.size() && !m_dirtiesAllStyle; ++i)
    42         analyzeStyleSheet(sheets[i]);
    43 }
    44 
    45 static bool determineSelectorScopes(const CSSSelectorList& selectorList, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes)
    46 {
    47     for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) {
    48         const CSSSelector* scopeSelector = 0;
    49         // This picks the widest scope, not the narrowest, to minimize the number of found scopes.
    50         for (const CSSSelector* current = selector; current; current = current->tagHistory()) {
    51             // Prefer ids over classes.
    52             if (current->m_match == CSSSelector::Id)
    53                 scopeSelector = current;
    54             else if (current->m_match == CSSSelector::Class && (!scopeSelector || scopeSelector->m_match != CSSSelector::Id))
    55                 scopeSelector = current;
    56             CSSSelector::Relation relation = current->relation();
    57             if (relation != CSSSelector::Descendant && relation != CSSSelector::Child && relation != CSSSelector::SubSelector)
    58                 break;
     41    for (auto& rule : rules) {
     42        if (rule->isMediaRule()) {
     43            if (shouldDirtyAllStyle(static_cast<StyleRuleMedia&>(*rule).childRules()))
     44                return true;
     45            continue;
    5946        }
    60         if (!scopeSelector)
    61             return false;
    62         ASSERT(scopeSelector->m_match == CSSSelector::Class || scopeSelector->m_match == CSSSelector::Id);
    63         if (scopeSelector->m_match == CSSSelector::Id)
    64             idScopes.add(scopeSelector->value().impl());
    65         else
    66             classScopes.add(scopeSelector->value().impl());
    67     }
    68     return true;
    69 }
    70 
    71 void StyleInvalidationAnalysis::analyzeStyleSheet(StyleSheetContents* styleSheetContents)
    72 {
    73     ASSERT(!styleSheetContents->isLoading());
    74 
    75     // See if all rules on the sheet are scoped to some specific ids or classes.
    76     // Then test if we actually have any of those in the tree at the moment.
    77     const Vector<RefPtr<StyleRuleImport>>& importRules = styleSheetContents->importRules();
    78     for (unsigned i = 0; i < importRules.size(); ++i) {
    79         if (!importRules[i]->styleSheet())
    80             continue;
    81         analyzeStyleSheet(importRules[i]->styleSheet());
    82         if (m_dirtiesAllStyle)
    83             return;
    84     }
    85     const Vector<RefPtr<StyleRuleBase>>& rules = styleSheetContents->childRules();
    86     for (unsigned i = 0; i < rules.size(); i++) {
    87         StyleRuleBase* rule = rules[i].get();
    88         if (!rule->isStyleRule()) {
    89             // FIXME: Media rules and maybe some others could be allowed.
    90             m_dirtiesAllStyle = true;
    91             return;
    92         }
    93         StyleRule* styleRule = static_cast<StyleRule*>(rule);
    94         if (!determineSelectorScopes(styleRule->selectorList(), m_idScopes, m_classScopes)) {
    95             m_dirtiesAllStyle = true;
    96             return;
    97         }
    98     }
    99 }
    100 
    101 static bool elementMatchesSelectorScopes(const Element& element, const HashSet<AtomicStringImpl*>& idScopes, const HashSet<AtomicStringImpl*>& classScopes)
    102 {
    103     if (!idScopes.isEmpty() && element.hasID() && idScopes.contains(element.idForStyleResolution().impl()))
    104         return true;
    105     if (classScopes.isEmpty() || !element.hasClass())
    106         return false;
    107     const SpaceSplitString& classNames = element.classNames();
    108     for (unsigned i = 0; i < classNames.size(); ++i) {
    109         if (classScopes.contains(classNames[i].impl()))
     47        // FIXME: At least font faces don't need full recalc in all cases.
     48        if (!rule->isStyleRule())
    11049            return true;
    11150    }
     
    11352}
    11453
     54static bool shouldDirtyAllStyle(const StyleSheetContents& sheet)
     55{
     56    for (auto& import : sheet.importRules()) {
     57        if (!import->styleSheet())
     58            continue;
     59        if (shouldDirtyAllStyle(*import->styleSheet()))
     60            return true;
     61    }
     62    if (shouldDirtyAllStyle(sheet.childRules()))
     63        return true;
     64    return false;
     65}
     66
     67static bool shouldDirtyAllStyle(const Vector<StyleSheetContents*>& sheets)
     68{
     69    for (auto& sheet : sheets) {
     70        if (shouldDirtyAllStyle(*sheet))
     71            return true;
     72    }
     73    return false;
     74}
     75
     76StyleInvalidationAnalysis::StyleInvalidationAnalysis(const Vector<StyleSheetContents*>& sheets, const MediaQueryEvaluator& mediaQueryEvaluator)
     77    : m_dirtiesAllStyle(shouldDirtyAllStyle(sheets))
     78{
     79    if (m_dirtiesAllStyle)
     80        return;
     81
     82    m_ruleSets.resetAuthorStyle();
     83    for (auto& sheet : sheets)
     84        m_ruleSets.authorStyle()->addRulesFromSheet(sheet, mediaQueryEvaluator);
     85
     86    // FIXME: We don't descent into shadow trees or otherwise handle shadow pseudo elements.
     87    if (m_ruleSets.authorStyle()->hasShadowPseudoElementRules())
     88        m_dirtiesAllStyle = true;
     89}
     90
     91static void invalidateStyleRecursively(Element& element, SelectorFilter& filter, const DocumentRuleSets& ruleSets)
     92{
     93    if (element.styleChangeType() > InlineStyleChange)
     94        return;
     95    if (element.styleChangeType() == NoStyleChange) {
     96        ElementRuleCollector ruleCollector(element, nullptr, ruleSets, filter);
     97        ruleCollector.setMode(SelectorChecker::StyleInvalidation);
     98        ruleCollector.matchAuthorRules(false);
     99
     100        if (ruleCollector.hasMatchedRules())
     101            element.setNeedsStyleRecalc(InlineStyleChange);
     102    }
     103
     104    auto children = childrenOfType<Element>(element);
     105    if (!children.first())
     106        return;
     107    filter.pushParent(&element);
     108    for (auto& child : children)
     109        invalidateStyleRecursively(child, filter, ruleSets);
     110    filter.popParent();
     111}
     112
    115113void StyleInvalidationAnalysis::invalidateStyle(Document& document)
    116114{
    117115    ASSERT(!m_dirtiesAllStyle);
    118     if (m_idScopes.isEmpty() && m_classScopes.isEmpty())
     116    if (!m_ruleSets.authorStyle())
    119117        return;
    120118
    121     auto it = descendantsOfType<Element>(document).begin();
    122     auto end = descendantsOfType<Element>(document).end();
    123     while (it != end) {
    124         if (elementMatchesSelectorScopes(*it, m_idScopes, m_classScopes)) {
    125             it->setNeedsStyleRecalc();
    126             // The whole subtree is now invalidated, we can skip to the next sibling.
    127             it.traverseNextSkippingChildren();
    128             continue;
    129         }
    130         ++it;
    131     }
     119    Element* documentElement = document.documentElement();
     120    if (!documentElement)
     121        return;
     122
     123    SelectorFilter filter;
     124    filter.setupParentStack(documentElement);
     125    invalidateStyleRecursively(*documentElement, filter, m_ruleSets);
    132126}
    133127
  • trunk/Source/WebCore/css/StyleInvalidationAnalysis.h

    r162770 r163592  
    11/*
    2  * Copyright (C) 2012 Apple Inc. All rights reserved.
     2 * Copyright (C) 2012, 2014 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2727#define StyleInvalidationAnalysis_h
    2828
     29#include "DocumentRuleSets.h"
    2930#include <wtf/HashSet.h>
    3031#include <wtf/text/AtomicStringImpl.h>
     
    3738class StyleInvalidationAnalysis {
    3839public:
    39     StyleInvalidationAnalysis(const Vector<StyleSheetContents*>&);
     40    StyleInvalidationAnalysis(const Vector<StyleSheetContents*>&, const MediaQueryEvaluator&);
    4041
    4142    bool dirtiesAllStyle() const { return m_dirtiesAllStyle; }
     
    4344
    4445private:
    45 
    46     void analyzeStyleSheet(StyleSheetContents*);
    47 
    4846    bool m_dirtiesAllStyle;
    49     HashSet<AtomicStringImpl*> m_idScopes;
    50     HashSet<AtomicStringImpl*> m_classScopes;
     47    DocumentRuleSets m_ruleSets;
    5148};
    5249
  • trunk/Source/WebCore/css/StyleResolver.cpp

    r163560 r163592  
    36583658}
    36593659
    3660 inline StyleResolver::MatchedProperties::~MatchedProperties()
     3660StyleResolver::MatchedProperties::~MatchedProperties()
    36613661{
    36623662}
  • trunk/Source/WebCore/css/StyleResolver.h

    r163440 r163592  
    169169    SelectorFilter& selectorFilter() { return m_selectorFilter; }
    170170
     171    const MediaQueryEvaluator& mediaQueryEvaluator() const { return *m_medium; }
     172
    171173private:
    172174    void initElement(Element*);
  • trunk/Source/WebCore/dom/DocumentStyleSheetCollection.cpp

    r163440 r163592  
    367367    if (!m_document.styleResolverIfExists())
    368368        return;
     369    StyleResolver& styleResolver = *m_document.styleResolverIfExists();
    369370
    370371    // Find out which stylesheets are new.
     
    397398    if (!m_document.body() || m_document.hasNodesWithPlaceholderStyle())
    398399        return;
    399     StyleInvalidationAnalysis invalidationAnalysis(addedSheets);
     400    StyleInvalidationAnalysis invalidationAnalysis(addedSheets, styleResolver.mediaQueryEvaluator());
    400401    if (invalidationAnalysis.dirtiesAllStyle())
    401402        return;
Note: See TracChangeset for help on using the changeset viewer.