void RuleFeatureSet::updateInvalidationSets(const RuleData& ruleData) { InvalidationSetFeatures features; auto result = extractInvalidationSetFeatures(ruleData.selector(), features, false); if (result.first) { features.forceSubtree = result.second == ForceSubtree; addFeaturesToInvalidationSets(*result.first, features); } // If any ::before and ::after rules specify 'content: attr(...)', we // need to create invalidation sets for those attributes. if (features.hasBeforeOrAfter) updateInvalidationSetsForContentAttribute(ruleData); }
std::pair<const CSSSelector*, RuleFeatureSet::UseFeaturesType> RuleFeatureSet::extractInvalidationSetFeatures(const CSSSelector& selector, InvalidationSetFeatures& features, PositionType position, CSSSelector::PseudoType pseudo) { bool foundFeatures = false; for (const CSSSelector* current = &selector; current; current = current->tagHistory()) { if (pseudo != CSSSelector::PseudoNot) foundFeatures |= extractInvalidationSetFeature(*current, features); // Initialize the entry in the invalidation set map, if supported. if (InvalidationSet* invalidationSet = invalidationSetForSelector(*current, InvalidateDescendants)) { if (position == Subject) invalidationSet->setInvalidatesSelf(); } else { if (requiresSubtreeInvalidation(*current)) { // Fall back to use subtree invalidations, even for features in the // rightmost compound selector. Returning the start &selector here // will make addFeaturesToInvalidationSets start marking invalidation // sets for subtree recalc for features in the rightmost compound // selector. return std::make_pair(&selector, ForceSubtree); } if (const CSSSelectorList* selectorList = current->selectorList()) { ASSERT(supportsInvalidationWithSelectorList(current->pseudoType())); const CSSSelector* subSelector = selectorList->first(); bool allSubSelectorsHaveFeatures = !!subSelector; for (; subSelector; subSelector = CSSSelectorList::next(*subSelector)) { auto result = extractInvalidationSetFeatures(*subSelector, features, position, current->pseudoType()); if (result.first) { // A non-null selector return means the sub-selector contained a // selector which requiresSubtreeInvalidation(). Return the rightmost // selector to mark for subtree recalcs like above. return std::make_pair(&selector, ForceSubtree); } allSubSelectorsHaveFeatures &= result.second == UseFeatures; } foundFeatures |= allSubSelectorsHaveFeatures; } } if (current->relation() == CSSSelector::SubSelector) continue; features.treeBoundaryCrossing = current->isShadowSelector(); features.adjacent = current->isAdjacentSelector(); if (current->relation() == CSSSelector::DirectAdjacent) features.maxDirectAdjacentSelectors = 1; return std::make_pair(current->tagHistory(), foundFeatures ? UseFeatures : ForceSubtree); } return std::make_pair(nullptr, foundFeatures ? UseFeatures : ForceSubtree); }
RuleFeatureSet::InvalidationSetMode RuleFeatureSet::updateInvalidationSets(const CSSSelector& selector) { InvalidationSetMode mode = invalidationSetModeForSelector(selector); if (mode != AddFeatures) return mode; InvalidationSetFeatures features; const CSSSelector* current = extractInvalidationSetFeatures(selector, features); if (current) { bool wholeSubtree = current->relation() == CSSSelector::DirectAdjacent || current->relation() == CSSSelector::IndirectAdjacent; current = current->tagHistory(); if (current) addFeaturesToInvalidationSets(*current, features, wholeSubtree); } return AddFeatures; }
const CSSSelector* RuleFeatureSet::extractInvalidationSetFeatures(const CSSSelector& selector, InvalidationSetFeatures& features) { const CSSSelector* lastSelector = &selector; for (; lastSelector; lastSelector = lastSelector->tagHistory()) { extractInvalidationSetFeature(*lastSelector, features); // Initialize the entry in the invalidation set map, if supported. invalidationSetForSelector(*lastSelector); if (lastSelector->pseudoType() == CSSSelector::PseudoHost || lastSelector->pseudoType() == CSSSelector::PseudoAny) { if (const CSSSelectorList* selectorList = lastSelector->selectorList()) { for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(*selector)) extractInvalidationSetFeatures(*selector, features); } } if (lastSelector->relation() != CSSSelector::SubSelector) break; } return lastSelector; }
// selector is the selector immediately to the left of the rightmost combinator. // siblingFeatures is null if selector is not immediately to the left of a sibling combinator. // descendantFeatures has the features of the rightmost compound selector. void RuleFeatureSet::addFeaturesToInvalidationSets(const CSSSelector* selector, InvalidationSetFeatures* siblingFeatures, InvalidationSetFeatures& descendantFeatures) { const CSSSelector* lastCompoundSelectorInAdjacentChain = selector; // We set siblingFeatures to &localFeatures if we find a rightmost sibling combinator. InvalidationSetFeatures localFeatures; for (const CSSSelector* current = selector; current; current = current->tagHistory()) { InvalidationType type = siblingFeatures ? InvalidateSiblings : InvalidateDescendants; if (InvalidationSet* invalidationSet = invalidationSetForSelector(*current, type)) { if (siblingFeatures) { SiblingInvalidationSet* siblingInvalidationSet = toSiblingInvalidationSet(invalidationSet); siblingInvalidationSet->updateMaxDirectAdjacentSelectors(siblingFeatures->maxDirectAdjacentSelectors); addFeaturesToInvalidationSet(*invalidationSet, *siblingFeatures); if (siblingFeatures == &descendantFeatures) siblingInvalidationSet->descendants().setInvalidatesSelf(); else addFeaturesToInvalidationSet(siblingInvalidationSet->descendants(), descendantFeatures); } else { addFeaturesToInvalidationSet(*invalidationSet, descendantFeatures); } } else { if (current->isHostPseudoClass()) descendantFeatures.treeBoundaryCrossing = true; if (current->isInsertionPointCrossing()) descendantFeatures.insertionPointCrossing = true; if (const CSSSelectorList* selectorList = current->selectorList()) { ASSERT(supportsInvalidationWithSelectorList(current->pseudoType())); for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(*subSelector)) addFeaturesToInvalidationSets(subSelector, siblingFeatures, descendantFeatures); } } if (current->relation() == CSSSelector::SubSelector) continue; if (current->isShadowSelector()) descendantFeatures.treeBoundaryCrossing = true; if (!current->isAdjacentSelector()) { lastCompoundSelectorInAdjacentChain = current->tagHistory(); siblingFeatures = nullptr; continue; } if (siblingFeatures) { if (siblingFeatures->maxDirectAdjacentSelectors == UINT_MAX) continue; if (current->relation() == CSSSelector::DirectAdjacent) siblingFeatures->maxDirectAdjacentSelectors++; else siblingFeatures->maxDirectAdjacentSelectors = UINT_MAX; continue; } localFeatures = InvalidationSetFeatures(); auto result = extractInvalidationSetFeatures(*lastCompoundSelectorInAdjacentChain, localFeatures, Ancestor); ASSERT(result.first); localFeatures.forceSubtree = result.second == ForceSubtree; siblingFeatures = &localFeatures; } }