Home | History | Annotate | Download | only in css
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 2004-2005 Allan Sandfeld Jensen (kde (at) carewolf.com)
      4  * Copyright (C) 2006, 2007 Nicholas Shanks (webkit (at) nickshanks.com)
      5  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
      6  * Copyright (C) 2007 Alexey Proskuryakov <ap (at) webkit.org>
      7  * Copyright (C) 2007, 2008 Eric Seidel <eric (at) webkit.org>
      8  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
      9  * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
     10  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
     11  * Copyright (C) 2012 Google Inc. All rights reserved.
     12  *
     13  * This library is free software; you can redistribute it and/or
     14  * modify it under the terms of the GNU Library General Public
     15  * License as published by the Free Software Foundation; either
     16  * version 2 of the License, or (at your option) any later version.
     17  *
     18  * This library is distributed in the hope that it will be useful,
     19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     21  * Library General Public License for more details.
     22  *
     23  * You should have received a copy of the GNU Library General Public License
     24  * along with this library; see the file COPYING.LIB.  If not, write to
     25  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     26  * Boston, MA 02110-1301, USA.
     27  */
     28 
     29 #include "config.h"
     30 #include "core/css/RuleFeature.h"
     31 
     32 #include "core/HTMLNames.h"
     33 #include "core/css/CSSSelector.h"
     34 #include "core/css/CSSSelectorList.h"
     35 #include "core/css/RuleSet.h"
     36 #include "core/css/StyleRule.h"
     37 #include "core/css/invalidation/DescendantInvalidationSet.h"
     38 #include "core/dom/Element.h"
     39 #include "core/dom/Node.h"
     40 #include "platform/RuntimeEnabledFeatures.h"
     41 #include "wtf/BitVector.h"
     42 
     43 namespace WebCore {
     44 
     45 static bool isSkippableComponentForInvalidation(const CSSSelector& selector)
     46 {
     47     if (selector.match() == CSSSelector::Tag
     48         || selector.match() == CSSSelector::Id
     49         || selector.isAttributeSelector())
     50         return true;
     51     if (selector.match() == CSSSelector::PseudoElement) {
     52         switch (selector.pseudoType()) {
     53         case CSSSelector::PseudoBefore:
     54         case CSSSelector::PseudoAfter:
     55         case CSSSelector::PseudoBackdrop:
     56         case CSSSelector::PseudoShadow:
     57             return true;
     58         default:
     59             return selector.isCustomPseudoElement();
     60         }
     61     }
     62     if (selector.match() != CSSSelector::PseudoClass)
     63         return false;
     64     switch (selector.pseudoType()) {
     65     case CSSSelector::PseudoEmpty:
     66     case CSSSelector::PseudoFirstChild:
     67     case CSSSelector::PseudoFirstOfType:
     68     case CSSSelector::PseudoLastChild:
     69     case CSSSelector::PseudoLastOfType:
     70     case CSSSelector::PseudoOnlyChild:
     71     case CSSSelector::PseudoOnlyOfType:
     72     case CSSSelector::PseudoNthChild:
     73     case CSSSelector::PseudoNthOfType:
     74     case CSSSelector::PseudoNthLastChild:
     75     case CSSSelector::PseudoNthLastOfType:
     76     case CSSSelector::PseudoLink:
     77     case CSSSelector::PseudoVisited:
     78     case CSSSelector::PseudoAnyLink:
     79     case CSSSelector::PseudoHover:
     80     case CSSSelector::PseudoDrag:
     81     case CSSSelector::PseudoFocus:
     82     case CSSSelector::PseudoActive:
     83     case CSSSelector::PseudoChecked:
     84     case CSSSelector::PseudoEnabled:
     85     case CSSSelector::PseudoDefault:
     86     case CSSSelector::PseudoDisabled:
     87     case CSSSelector::PseudoOptional:
     88     case CSSSelector::PseudoRequired:
     89     case CSSSelector::PseudoReadOnly:
     90     case CSSSelector::PseudoReadWrite:
     91     case CSSSelector::PseudoValid:
     92     case CSSSelector::PseudoInvalid:
     93     case CSSSelector::PseudoIndeterminate:
     94     case CSSSelector::PseudoTarget:
     95     case CSSSelector::PseudoLang:
     96     case CSSSelector::PseudoRoot:
     97     case CSSSelector::PseudoScope:
     98     case CSSSelector::PseudoInRange:
     99     case CSSSelector::PseudoOutOfRange:
    100     case CSSSelector::PseudoUnresolved:
    101         return true;
    102     default:
    103         return false;
    104     }
    105 }
    106 
    107 RuleFeature::RuleFeature(StyleRule* rule, unsigned selectorIndex, bool hasDocumentSecurityOrigin)
    108     : rule(rule)
    109     , selectorIndex(selectorIndex)
    110     , hasDocumentSecurityOrigin(hasDocumentSecurityOrigin)
    111 {
    112 }
    113 
    114 void RuleFeature::trace(Visitor* visitor)
    115 {
    116     visitor->trace(rule);
    117 }
    118 
    119 // This method is somewhat conservative in what it accepts.
    120 RuleFeatureSet::InvalidationSetMode RuleFeatureSet::invalidationSetModeForSelector(const CSSSelector& selector)
    121 {
    122     bool foundDescendantRelation = false;
    123     bool foundIdent = false;
    124     for (const CSSSelector* component = &selector; component; component = component->tagHistory()) {
    125 
    126         if (component->match() == CSSSelector::Class || component->match() == CSSSelector::Id
    127             || (component->match() == CSSSelector::Tag && component->tagQName().localName() != starAtom)
    128             || component->isAttributeSelector() || component->isCustomPseudoElement()) {
    129             if (!foundDescendantRelation)
    130                 foundIdent = true;
    131         } else if (component->pseudoType() == CSSSelector::PseudoHost || component->pseudoType() == CSSSelector::PseudoAny) {
    132             if (const CSSSelectorList* selectorList = component->selectorList()) {
    133                 for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(*selector)) {
    134                     InvalidationSetMode hostMode = invalidationSetModeForSelector(*selector);
    135                     if (hostMode == UseSubtreeStyleChange)
    136                         return foundDescendantRelation ? UseLocalStyleChange : UseSubtreeStyleChange;
    137                     if (!foundDescendantRelation && hostMode == AddFeatures)
    138                         foundIdent = true;
    139                 }
    140             }
    141         } else if (!isSkippableComponentForInvalidation(*component)) {
    142             return foundDescendantRelation ? UseLocalStyleChange : UseSubtreeStyleChange;
    143         }
    144         switch (component->relation()) {
    145         case CSSSelector::Descendant:
    146         case CSSSelector::Child:
    147         case CSSSelector::ShadowPseudo:
    148         case CSSSelector::ShadowDeep:
    149             foundDescendantRelation = true;
    150             // Fall through!
    151         case CSSSelector::SubSelector:
    152         case CSSSelector::DirectAdjacent:
    153         case CSSSelector::IndirectAdjacent:
    154             continue;
    155         default:
    156             // All combinators should be handled above.
    157             ASSERT_NOT_REACHED();
    158             return UseLocalStyleChange;
    159         }
    160     }
    161     return foundIdent ? AddFeatures : UseLocalStyleChange;
    162 }
    163 
    164 void RuleFeatureSet::extractInvalidationSetFeature(const CSSSelector& selector, InvalidationSetFeatures& features)
    165 {
    166     if (selector.match() == CSSSelector::Tag)
    167         features.tagName = selector.tagQName().localName();
    168     else if (selector.match() == CSSSelector::Id)
    169         features.id = selector.value();
    170     else if (selector.match() == CSSSelector::Class)
    171         features.classes.append(selector.value());
    172     else if (selector.isAttributeSelector())
    173         features.attributes.append(selector.attribute().localName());
    174     else if (selector.isCustomPseudoElement())
    175         features.customPseudoElement = true;
    176 }
    177 
    178 RuleFeatureSet::RuleFeatureSet()
    179     : m_targetedStyleRecalcEnabled(RuntimeEnabledFeatures::targetedStyleRecalcEnabled())
    180 {
    181 }
    182 
    183 RuleFeatureSet::~RuleFeatureSet()
    184 {
    185 }
    186 
    187 DescendantInvalidationSet* RuleFeatureSet::invalidationSetForSelector(const CSSSelector& selector)
    188 {
    189     if (selector.match() == CSSSelector::Class)
    190         return &ensureClassInvalidationSet(selector.value());
    191     if (selector.isAttributeSelector())
    192         return &ensureAttributeInvalidationSet(selector.attribute().localName());
    193     if (selector.match() == CSSSelector::Id)
    194         return &ensureIdInvalidationSet(selector.value());
    195     if (selector.match() == CSSSelector::PseudoClass) {
    196         CSSSelector::PseudoType pseudo = selector.pseudoType();
    197         if (pseudo == CSSSelector::PseudoHover || pseudo == CSSSelector::PseudoActive || pseudo == CSSSelector::PseudoFocus)
    198             return &ensurePseudoInvalidationSet(pseudo);
    199     }
    200     return 0;
    201 }
    202 
    203 RuleFeatureSet::InvalidationSetMode RuleFeatureSet::updateInvalidationSets(const CSSSelector& selector)
    204 {
    205     InvalidationSetMode mode = invalidationSetModeForSelector(selector);
    206     if (mode != AddFeatures)
    207         return mode;
    208 
    209     InvalidationSetFeatures features;
    210     if (const CSSSelector* current = extractInvalidationSetFeatures(selector, features))
    211         addFeaturesToInvalidationSets(*current, features);
    212     return AddFeatures;
    213 }
    214 
    215 const CSSSelector* RuleFeatureSet::extractInvalidationSetFeatures(const CSSSelector& selector, InvalidationSetFeatures& features)
    216 {
    217     for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
    218         extractInvalidationSetFeature(*current, features);
    219         // Initialize the entry in the invalidation set map, if supported.
    220         invalidationSetForSelector(*current);
    221         if (current->pseudoType() == CSSSelector::PseudoHost || current->pseudoType() == CSSSelector::PseudoAny) {
    222             if (const CSSSelectorList* selectorList = current->selectorList()) {
    223                 for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(*selector))
    224                     extractInvalidationSetFeatures(*selector, features);
    225             }
    226         }
    227 
    228         switch (current->relation()) {
    229         case CSSSelector::SubSelector:
    230             break;
    231         case CSSSelector::ShadowPseudo:
    232         case CSSSelector::ShadowDeep:
    233             features.treeBoundaryCrossing = true;
    234             return current->tagHistory();
    235         case CSSSelector::DirectAdjacent:
    236         case CSSSelector::IndirectAdjacent:
    237             features.wholeSubtree = true;
    238             return current->tagHistory();
    239         case CSSSelector::Descendant:
    240         case CSSSelector::Child:
    241             return current->tagHistory();
    242         }
    243     }
    244     return 0;
    245 }
    246 
    247 void RuleFeatureSet::addFeaturesToInvalidationSets(const CSSSelector& selector, InvalidationSetFeatures& features)
    248 {
    249     for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
    250         if (DescendantInvalidationSet* invalidationSet = invalidationSetForSelector(*current)) {
    251             if (features.treeBoundaryCrossing)
    252                 invalidationSet->setTreeBoundaryCrossing();
    253             if (features.wholeSubtree) {
    254                 invalidationSet->setWholeSubtreeInvalid();
    255             } else {
    256                 if (!features.id.isEmpty())
    257                     invalidationSet->addId(features.id);
    258                 if (!features.tagName.isEmpty())
    259                     invalidationSet->addTagName(features.tagName);
    260                 for (Vector<AtomicString>::const_iterator it = features.classes.begin(); it != features.classes.end(); ++it)
    261                     invalidationSet->addClass(*it);
    262                 for (Vector<AtomicString>::const_iterator it = features.attributes.begin(); it != features.attributes.end(); ++it)
    263                     invalidationSet->addAttribute(*it);
    264                 if (features.customPseudoElement)
    265                     invalidationSet->setCustomPseudoInvalid();
    266             }
    267         } else if (current->pseudoType() == CSSSelector::PseudoHost || current->pseudoType() == CSSSelector::PseudoAny) {
    268             if (current->pseudoType() == CSSSelector::PseudoHost)
    269                 features.treeBoundaryCrossing = true;
    270             if (const CSSSelectorList* selectorList = current->selectorList()) {
    271                 for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(*selector))
    272                     addFeaturesToInvalidationSets(*selector, features);
    273             }
    274         }
    275         switch (current->relation()) {
    276         case CSSSelector::SubSelector:
    277             break;
    278         case CSSSelector::ShadowPseudo:
    279         case CSSSelector::ShadowDeep:
    280             features.treeBoundaryCrossing = true;
    281             features.wholeSubtree = false;
    282             break;
    283         case CSSSelector::Descendant:
    284         case CSSSelector::Child:
    285             features.wholeSubtree = false;
    286             break;
    287         case CSSSelector::DirectAdjacent:
    288         case CSSSelector::IndirectAdjacent:
    289             features.wholeSubtree = true;
    290             break;
    291         }
    292     }
    293 }
    294 
    295 void RuleFeatureSet::addContentAttr(const AtomicString& attributeName)
    296 {
    297     DescendantInvalidationSet& invalidationSet = ensureAttributeInvalidationSet(attributeName);
    298     invalidationSet.setWholeSubtreeInvalid();
    299 }
    300 
    301 void RuleFeatureSet::collectFeaturesFromRuleData(const RuleData& ruleData)
    302 {
    303     FeatureMetadata metadata;
    304     InvalidationSetMode mode = UseSubtreeStyleChange;
    305     if (m_targetedStyleRecalcEnabled)
    306         mode = updateInvalidationSets(ruleData.selector());
    307 
    308     collectFeaturesFromSelector(ruleData.selector(), metadata, mode);
    309     m_metadata.add(metadata);
    310 
    311     if (metadata.foundSiblingSelector)
    312         siblingRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
    313     if (ruleData.containsUncommonAttributeSelector())
    314         uncommonAttributeRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
    315 }
    316 
    317 DescendantInvalidationSet& RuleFeatureSet::ensureClassInvalidationSet(const AtomicString& className)
    318 {
    319     InvalidationSetMap::AddResult addResult = m_classInvalidationSets.add(className, nullptr);
    320     if (addResult.isNewEntry)
    321         addResult.storedValue->value = DescendantInvalidationSet::create();
    322     return *addResult.storedValue->value;
    323 }
    324 
    325 DescendantInvalidationSet& RuleFeatureSet::ensureAttributeInvalidationSet(const AtomicString& attributeName)
    326 {
    327     InvalidationSetMap::AddResult addResult = m_attributeInvalidationSets.add(attributeName, nullptr);
    328     if (addResult.isNewEntry)
    329         addResult.storedValue->value = DescendantInvalidationSet::create();
    330     return *addResult.storedValue->value;
    331 }
    332 
    333 DescendantInvalidationSet& RuleFeatureSet::ensureIdInvalidationSet(const AtomicString& id)
    334 {
    335     InvalidationSetMap::AddResult addResult = m_idInvalidationSets.add(id, nullptr);
    336     if (addResult.isNewEntry)
    337         addResult.storedValue->value = DescendantInvalidationSet::create();
    338     return *addResult.storedValue->value;
    339 }
    340 
    341 DescendantInvalidationSet& RuleFeatureSet::ensurePseudoInvalidationSet(CSSSelector::PseudoType pseudoType)
    342 {
    343     PseudoTypeInvalidationSetMap::AddResult addResult = m_pseudoInvalidationSets.add(pseudoType, nullptr);
    344     if (addResult.isNewEntry)
    345         addResult.storedValue->value = DescendantInvalidationSet::create();
    346     return *addResult.storedValue->value;
    347 }
    348 
    349 void RuleFeatureSet::collectFeaturesFromSelector(const CSSSelector& selector)
    350 {
    351     collectFeaturesFromSelector(selector, m_metadata, UseSubtreeStyleChange);
    352 }
    353 
    354 void RuleFeatureSet::collectFeaturesFromSelector(const CSSSelector& selector, RuleFeatureSet::FeatureMetadata& metadata, InvalidationSetMode mode)
    355 {
    356     unsigned maxDirectAdjacentSelectors = 0;
    357 
    358     for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
    359         if (mode != AddFeatures) {
    360             if (DescendantInvalidationSet* invalidationSet = invalidationSetForSelector(*current)) {
    361                 if (mode == UseSubtreeStyleChange)
    362                     invalidationSet->setWholeSubtreeInvalid();
    363             }
    364         }
    365         if (current->pseudoType() == CSSSelector::PseudoFirstLine)
    366             metadata.usesFirstLineRules = true;
    367         if (current->isDirectAdjacentSelector()) {
    368             maxDirectAdjacentSelectors++;
    369         } else if (maxDirectAdjacentSelectors) {
    370             if (maxDirectAdjacentSelectors > metadata.maxDirectAdjacentSelectors)
    371                 metadata.maxDirectAdjacentSelectors = maxDirectAdjacentSelectors;
    372             maxDirectAdjacentSelectors = 0;
    373         }
    374         if (current->isSiblingSelector())
    375             metadata.foundSiblingSelector = true;
    376 
    377         collectFeaturesFromSelectorList(current->selectorList(), metadata, mode);
    378 
    379         if (mode == UseLocalStyleChange && current->relation() != CSSSelector::SubSelector)
    380             mode = UseSubtreeStyleChange;
    381     }
    382 
    383     ASSERT(!maxDirectAdjacentSelectors);
    384 }
    385 
    386 void RuleFeatureSet::collectFeaturesFromSelectorList(const CSSSelectorList* selectorList, RuleFeatureSet::FeatureMetadata& metadata, InvalidationSetMode mode)
    387 {
    388     if (!selectorList)
    389         return;
    390 
    391     for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(*selector))
    392         collectFeaturesFromSelector(*selector, metadata, mode);
    393 }
    394 
    395 void RuleFeatureSet::FeatureMetadata::add(const FeatureMetadata& other)
    396 {
    397     usesFirstLineRules = usesFirstLineRules || other.usesFirstLineRules;
    398     maxDirectAdjacentSelectors = std::max(maxDirectAdjacentSelectors, other.maxDirectAdjacentSelectors);
    399 }
    400 
    401 void RuleFeatureSet::FeatureMetadata::clear()
    402 {
    403     usesFirstLineRules = false;
    404     foundSiblingSelector = false;
    405     maxDirectAdjacentSelectors = 0;
    406 }
    407 
    408 void RuleFeatureSet::add(const RuleFeatureSet& other)
    409 {
    410     for (InvalidationSetMap::const_iterator it = other.m_classInvalidationSets.begin(); it != other.m_classInvalidationSets.end(); ++it)
    411         ensureClassInvalidationSet(it->key).combine(*it->value);
    412     for (InvalidationSetMap::const_iterator it = other.m_attributeInvalidationSets.begin(); it != other.m_attributeInvalidationSets.end(); ++it)
    413         ensureAttributeInvalidationSet(it->key).combine(*it->value);
    414     for (InvalidationSetMap::const_iterator it = other.m_idInvalidationSets.begin(); it != other.m_idInvalidationSets.end(); ++it)
    415         ensureIdInvalidationSet(it->key).combine(*it->value);
    416     for (PseudoTypeInvalidationSetMap::const_iterator it = other.m_pseudoInvalidationSets.begin(); it != other.m_pseudoInvalidationSets.end(); ++it)
    417         ensurePseudoInvalidationSet(static_cast<CSSSelector::PseudoType>(it->key)).combine(*it->value);
    418 
    419     m_metadata.add(other.m_metadata);
    420 
    421     siblingRules.appendVector(other.siblingRules);
    422     uncommonAttributeRules.appendVector(other.uncommonAttributeRules);
    423 }
    424 
    425 void RuleFeatureSet::clear()
    426 {
    427     siblingRules.clear();
    428     uncommonAttributeRules.clear();
    429     m_metadata.clear();
    430     m_classInvalidationSets.clear();
    431     m_attributeInvalidationSets.clear();
    432     m_idInvalidationSets.clear();
    433     // We cannot clear m_styleInvalidator here, because the style invalidator might not
    434     // have been evaluated yet. If not yet, in StyleInvalidator, there exists some element
    435     // who has needsStyleInvlidation but does not have any invalidation list.
    436     // This makes Blink not to recalc style correctly. crbug.com/344729.
    437 }
    438 
    439 void RuleFeatureSet::scheduleStyleInvalidationForClassChange(const SpaceSplitString& changedClasses, Element& element)
    440 {
    441     unsigned changedSize = changedClasses.size();
    442     for (unsigned i = 0; i < changedSize; ++i) {
    443         addClassToInvalidationSet(changedClasses[i], element);
    444     }
    445 }
    446 
    447 void RuleFeatureSet::scheduleStyleInvalidationForClassChange(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses, Element& element)
    448 {
    449     if (!oldClasses.size()) {
    450         scheduleStyleInvalidationForClassChange(newClasses, element);
    451         return;
    452     }
    453 
    454     // Class vectors tend to be very short. This is faster than using a hash table.
    455     BitVector remainingClassBits;
    456     remainingClassBits.ensureSize(oldClasses.size());
    457 
    458     for (unsigned i = 0; i < newClasses.size(); ++i) {
    459         bool found = false;
    460         for (unsigned j = 0; j < oldClasses.size(); ++j) {
    461             if (newClasses[i] == oldClasses[j]) {
    462                 // Mark each class that is still in the newClasses so we can skip doing
    463                 // an n^2 search below when looking for removals. We can't break from
    464                 // this loop early since a class can appear more than once.
    465                 remainingClassBits.quickSet(j);
    466                 found = true;
    467             }
    468         }
    469         // Class was added.
    470         if (!found)
    471             addClassToInvalidationSet(newClasses[i], element);
    472     }
    473 
    474     for (unsigned i = 0; i < oldClasses.size(); ++i) {
    475         if (remainingClassBits.quickGet(i))
    476             continue;
    477         // Class was removed.
    478         addClassToInvalidationSet(oldClasses[i], element);
    479     }
    480 }
    481 
    482 void RuleFeatureSet::scheduleStyleInvalidationForAttributeChange(const QualifiedName& attributeName, Element& element)
    483 {
    484 
    485     if (RefPtrWillBeRawPtr<DescendantInvalidationSet> invalidationSet = m_attributeInvalidationSets.get(attributeName.localName()))
    486         m_styleInvalidator.scheduleInvalidation(invalidationSet, element);
    487 }
    488 
    489 void RuleFeatureSet::scheduleStyleInvalidationForIdChange(const AtomicString& oldId, const AtomicString& newId, Element& element)
    490 {
    491     if (!oldId.isEmpty()) {
    492         if (RefPtrWillBeRawPtr<DescendantInvalidationSet> invalidationSet = m_idInvalidationSets.get(oldId))
    493             m_styleInvalidator.scheduleInvalidation(invalidationSet, element);
    494     }
    495     if (!newId.isEmpty()) {
    496         if (RefPtrWillBeRawPtr<DescendantInvalidationSet> invalidationSet = m_idInvalidationSets.get(newId))
    497             m_styleInvalidator.scheduleInvalidation(invalidationSet, element);
    498     }
    499 }
    500 
    501 void RuleFeatureSet::scheduleStyleInvalidationForPseudoChange(CSSSelector::PseudoType pseudo, Element& element)
    502 {
    503     if (RefPtrWillBeRawPtr<DescendantInvalidationSet> invalidationSet = m_pseudoInvalidationSets.get(pseudo))
    504         m_styleInvalidator.scheduleInvalidation(invalidationSet, element);
    505 }
    506 
    507 void RuleFeatureSet::addClassToInvalidationSet(const AtomicString& className, Element& element)
    508 {
    509     if (RefPtrWillBeRawPtr<DescendantInvalidationSet> invalidationSet = m_classInvalidationSets.get(className))
    510         m_styleInvalidator.scheduleInvalidation(invalidationSet, element);
    511 }
    512 
    513 StyleInvalidator& RuleFeatureSet::styleInvalidator()
    514 {
    515     return m_styleInvalidator;
    516 }
    517 
    518 void RuleFeatureSet::trace(Visitor* visitor)
    519 {
    520     visitor->trace(siblingRules);
    521     visitor->trace(uncommonAttributeRules);
    522     visitor->trace(m_classInvalidationSets);
    523     visitor->trace(m_attributeInvalidationSets);
    524     visitor->trace(m_idInvalidationSets);
    525     visitor->trace(m_pseudoInvalidationSets);
    526     visitor->trace(m_styleInvalidator);
    527 }
    528 
    529 } // namespace WebCore
    530