Commit 0f24a5ef authored by rune@opera.com's avatar rune@opera.com

Single code path for updating invalidation sets.

Allow all selectors to update invalidation sets using updateInvalidationSets
and never fall back to a separate wholeSubtree loop in
collectFeaturesFromSelector. The method for finding the InvalidationSetMode
is dropped completely.

With this change we are now at a point were a whole subtree invalidation is
only necessary for these cases:

1. There are no recognized features in the rightmost compound selector

 - A universal selector: ".a *"
 - Only contains pseudo classes or elements that are not considered
   features:

   ".a ::before", ".a :first-child", etc.

2. The invalidation set component is immediately left of an adjacent
   combinator:

   ".a + .b", ".a ~ .b"

3. The selector has a ::first-letter or ::first-line pseudo

   These are currently depending on style recalc or layout below the
   element they are pseudo elements for.

3. There is a :host-context() in the rightmost compound selector

   :host-context sub-selectors match ascendants of the :host element for
   which we are resolving style, so ":host-context(.a)" is effectively the
   same as ".a :host" which falls under no recognized features in the
   rightmost compound in point 1.

Points 1, 3, and 4 are now handled through
InvalidationSetFeatures::forceSubtree set as a result of feature extraction.
Point 2 is handled by updating InvalidationSetFeatures::adjacent as we
progress past selector combinators.

RuleFeaturesSet::collectFeaturesFromSelector is now back to how it was
before we introduced invalidation sets (without the feature hash sets).
Now, it only counts adjacent combinators and sets a flag when finding
::first-line, so it is possible to squash it into the same method as
updateInvalidationSets() to have a single pass over the selector when
finding rule features (if we want to do that).

The lowered expected node count in the :host-context test is due to the
fact that we only fall back to subtree invalidation when present in the
rightmost compound. ::first-letter/::first-line are always in the rightmost
compound, and :host-context() doesn't need subtree invalidation when in
other compounds. ":host-context(.a) .b" is effectively ".a :host .b".

R=chrishtr@chromium.org
BUG=335247

Review URL: https://codereview.chromium.org/726503003

git-svn-id: svn://svn.chromium.org/blink/trunk@185354 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 8e1c34db
...@@ -38,7 +38,7 @@ test(function(){ ...@@ -38,7 +38,7 @@ test(function(){
assert_equals(getComputedStyle(inner, "").backgroundColor, transparent, "Background color before class change."); assert_equals(getComputedStyle(inner, "").backgroundColor, transparent, "Background color before class change.");
t2.className = "t2"; t2.className = "t2";
if (window.internals) if (window.internals)
assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 8, "Element recalc count on class change."); assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 2, "Element recalc count on class change.");
assert_equals(getComputedStyle(inner, "").backgroundColor, green, "Background color after class change."); assert_equals(getComputedStyle(inner, "").backgroundColor, green, "Background color after class change.");
}, "Matching id descendant of :host-context with class."); }, "Matching id descendant of :host-context with class.");
......
...@@ -163,6 +163,15 @@ static bool supportsInvalidation(CSSSelector::PseudoType type) ...@@ -163,6 +163,15 @@ static bool supportsInvalidation(CSSSelector::PseudoType type)
} }
} }
static bool supportsInvalidationWithSelectorList(CSSSelector::PseudoType pseudo)
{
return pseudo == CSSSelector::PseudoAny
|| pseudo == CSSSelector::PseudoCue
|| pseudo == CSSSelector::PseudoHost
|| pseudo == CSSSelector::PseudoHostContext
|| pseudo == CSSSelector::PseudoNot;
}
#endif // ENABLE(ASSERT) #endif // ENABLE(ASSERT)
static bool requiresSubtreeInvalidation(const CSSSelector& selector) static bool requiresSubtreeInvalidation(const CSSSelector& selector)
...@@ -200,77 +209,9 @@ void RuleFeature::trace(Visitor* visitor) ...@@ -200,77 +209,9 @@ void RuleFeature::trace(Visitor* visitor)
visitor->trace(rule); visitor->trace(rule);
} }
static bool supportsInvalidationWithSelectorList(CSSSelector::PseudoType pseudo) bool RuleFeatureSet::extractInvalidationSetFeature(const CSSSelector& selector, InvalidationSetFeatures& features)
{
return pseudo == CSSSelector::PseudoAny
|| pseudo == CSSSelector::PseudoCue
|| pseudo == CSSSelector::PseudoHost
|| pseudo == CSSSelector::PseudoNot;
}
// This method is somewhat conservative in what it accepts.
RuleFeatureSet::InvalidationSetMode RuleFeatureSet::invalidationSetModeForSelector(const CSSSelector& selector)
{ {
bool foundCombinator = false; if (selector.match() == CSSSelector::Tag && selector.tagQName().localName() != starAtom)
bool foundIdent = false;
for (const CSSSelector* component = &selector; component; component = component->tagHistory()) {
if (component->match() == CSSSelector::Class || component->match() == CSSSelector::Id
|| (component->match() == CSSSelector::Tag && component->tagQName().localName() != starAtom)
|| component->isAttributeSelector() || component->isCustomPseudoElement()) {
if (!foundCombinator) {
// We have found an invalidation set feature in the rightmost compound selector.
foundIdent = true;
}
} else if (supportsInvalidationWithSelectorList(component->pseudoType())) {
if (const CSSSelectorList* selectorList = component->selectorList()) {
// Features inside :not() are not added to the feature set, so consider it a universal selector.
bool foundUniversal = component->pseudoType() == CSSSelector::PseudoNot;
for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(*selector)) {
// Find the invalidation set mode for each of the selectors in the selector list
// of a :not(), :host(), etc. For instance, ".x :-webkit-any(.a, .b)" yields an
// AddFeatures mode for both ".a" and ".b". ":-webkit-any(.a, *)" yields AddFeatures
// for ".a", but UseSubtreeStyleChange for "*". One sub-selector without invalidation
// set features is sufficient to cause the selector to be a universal selector as far
// the invalidation set is concerned.
InvalidationSetMode subSelectorMode = invalidationSetModeForSelector(*selector);
// The sub-selector contained something unskippable, fall back to whole subtree
// recalcs in collectFeaturesFromSelector. subSelectorMode will return
// UseSubtreeStyleChange since there are no combinators inside the selector list,
// so translate it to UseLocalStyleChange if a combinator has been seen in the
// outer context.
//
// FIXME: Is UseSubtreeStyleChange ever needed as input to collectFeaturesFromSelector?
// That is, are there any selectors for which we need to use SubtreeStyleChange for
// changing features when present in the rightmost compound selector?
if (subSelectorMode == UseSubtreeStyleChange)
return foundCombinator ? UseLocalStyleChange : UseSubtreeStyleChange;
// We found no features in the sub-selector, only skippable ones (foundIdent was
// false at the end of this method). That is a universal selector as far as the
// invalidation set is concerned.
if (subSelectorMode == UseLocalStyleChange)
foundUniversal = true;
}
if (!foundUniversal && !foundCombinator) {
// All sub-selectors contained invalidation set features and
// we are in the rightmost compound selector.
foundIdent = true;
}
}
} else if (requiresSubtreeInvalidation(*component)) {
return foundCombinator ? UseLocalStyleChange : UseSubtreeStyleChange;
}
if (component->relation() != CSSSelector::SubSelector)
foundCombinator = true;
}
return foundIdent ? AddFeatures : UseLocalStyleChange;
}
void RuleFeatureSet::extractInvalidationSetFeature(const CSSSelector& selector, InvalidationSetFeatures& features)
{
if (selector.match() == CSSSelector::Tag)
features.tagName = selector.tagQName().localName(); features.tagName = selector.tagQName().localName();
else if (selector.match() == CSSSelector::Id) else if (selector.match() == CSSSelector::Id)
features.id = selector.value(); features.id = selector.value();
...@@ -280,6 +221,9 @@ void RuleFeatureSet::extractInvalidationSetFeature(const CSSSelector& selector, ...@@ -280,6 +221,9 @@ void RuleFeatureSet::extractInvalidationSetFeature(const CSSSelector& selector,
features.attributes.append(selector.attribute().localName()); features.attributes.append(selector.attribute().localName());
else if (selector.isCustomPseudoElement()) else if (selector.isCustomPseudoElement())
features.customPseudoElement = true; features.customPseudoElement = true;
else
return false;
return true;
} }
RuleFeatureSet::RuleFeatureSet() RuleFeatureSet::RuleFeatureSet()
...@@ -321,38 +265,59 @@ DescendantInvalidationSet* RuleFeatureSet::invalidationSetForSelector(const CSSS ...@@ -321,38 +265,59 @@ DescendantInvalidationSet* RuleFeatureSet::invalidationSetForSelector(const CSSS
break; break;
} }
} }
return 0; return nullptr;
} }
// Given a selector, update the descendant invalidation sets for the features found // Given a selector, update the descendant invalidation sets for the features found
// in the selector. The first step is to extract the features from the rightmost // in the selector. The first step is to extract the features from the rightmost
// compound selector (extractInvalidationSetFeatures). Secondly, those features will be // compound selector (extractInvalidationSetFeatures). Secondly, add those features
// added to the invalidation sets for the features found in the other compound selectors // to the invalidation sets for the features found in the other compound selectors
// (addFeaturesToInvalidationSets). // (addFeaturesToInvalidationSets). If we find a feature in the right-most compound
// selector that requires a subtree recalc, we addFeaturesToInvalidationSets for the
// rightmost compound selector as well.
RuleFeatureSet::InvalidationSetMode RuleFeatureSet::updateInvalidationSets(const CSSSelector& selector) void RuleFeatureSet::updateInvalidationSets(const CSSSelector& selector)
{ {
InvalidationSetMode mode = invalidationSetModeForSelector(selector);
if (mode != AddFeatures)
return mode;
InvalidationSetFeatures features; InvalidationSetFeatures features;
if (const CSSSelector* current = extractInvalidationSetFeatures(selector, features, false)) auto result = extractInvalidationSetFeatures(selector, features, false);
addFeaturesToInvalidationSets(*current, features); if (result.first) {
return AddFeatures; features.forceSubtree = result.second == ForceSubtree;
addFeaturesToInvalidationSets(*result.first, features);
}
} }
const CSSSelector* RuleFeatureSet::extractInvalidationSetFeatures(const CSSSelector& selector, InvalidationSetFeatures& features, bool negated) std::pair<const CSSSelector*, RuleFeatureSet::UseFeaturesType>
RuleFeatureSet::extractInvalidationSetFeatures(const CSSSelector& selector, InvalidationSetFeatures& features, bool negated)
{ {
bool foundFeatures = false;
for (const CSSSelector* current = &selector; current; current = current->tagHistory()) { for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
if (!negated) if (!negated)
extractInvalidationSetFeature(*current, features); foundFeatures |= extractInvalidationSetFeature(*current, features);
// Initialize the entry in the invalidation set map, if supported. // Initialize the entry in the invalidation set map, if supported.
invalidationSetForSelector(*current); if (!invalidationSetForSelector(*current)) {
if (supportsInvalidationWithSelectorList(current->pseudoType())) { 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()) { if (const CSSSelectorList* selectorList = current->selectorList()) {
for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(*selector)) ASSERT(supportsInvalidationWithSelectorList(current->pseudoType()));
extractInvalidationSetFeatures(*selector, features, current->pseudoType() == CSSSelector::PseudoNot); const CSSSelector* subSelector = selectorList->first();
bool allSubSelectorsHaveFeatures = !!subSelector;
for (; subSelector; subSelector = CSSSelectorList::next(*subSelector)) {
auto result = extractInvalidationSetFeatures(*subSelector, features, current->pseudoType() == CSSSelector::PseudoNot);
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;
} }
} }
...@@ -361,9 +326,9 @@ const CSSSelector* RuleFeatureSet::extractInvalidationSetFeatures(const CSSSelec ...@@ -361,9 +326,9 @@ const CSSSelector* RuleFeatureSet::extractInvalidationSetFeatures(const CSSSelec
features.treeBoundaryCrossing = current->isShadowSelector(); features.treeBoundaryCrossing = current->isShadowSelector();
features.adjacent = current->isAdjacentSelector(); features.adjacent = current->isAdjacentSelector();
return current->tagHistory(); return std::make_pair(current->tagHistory(), foundFeatures ? UseFeatures : ForceSubtree);
} }
return 0; return std::make_pair(nullptr, ForceSubtree);
} }
// Add features extracted from the rightmost compound selector to descendant invalidation // Add features extracted from the rightmost compound selector to descendant invalidation
...@@ -384,7 +349,7 @@ void RuleFeatureSet::addFeaturesToInvalidationSet(DescendantInvalidationSet& inv ...@@ -384,7 +349,7 @@ void RuleFeatureSet::addFeaturesToInvalidationSet(DescendantInvalidationSet& inv
invalidationSet.setTreeBoundaryCrossing(); invalidationSet.setTreeBoundaryCrossing();
if (features.insertionPointCrossing) if (features.insertionPointCrossing)
invalidationSet.setInsertionPointCrossing(); invalidationSet.setInsertionPointCrossing();
if (features.adjacent) { if (features.useSubtreeInvalidation()) {
invalidationSet.setWholeSubtreeInvalid(); invalidationSet.setWholeSubtreeInvalid();
return; return;
} }
...@@ -434,10 +399,10 @@ void RuleFeatureSet::addContentAttr(const AtomicString& attributeName) ...@@ -434,10 +399,10 @@ void RuleFeatureSet::addContentAttr(const AtomicString& attributeName)
void RuleFeatureSet::collectFeaturesFromRuleData(const RuleData& ruleData) void RuleFeatureSet::collectFeaturesFromRuleData(const RuleData& ruleData)
{ {
FeatureMetadata metadata; updateInvalidationSets(ruleData.selector());
InvalidationSetMode mode = updateInvalidationSets(ruleData.selector());
collectFeaturesFromSelector(ruleData.selector(), metadata, mode); FeatureMetadata metadata;
collectFeaturesFromSelector(ruleData.selector(), metadata);
m_metadata.add(metadata); m_metadata.add(metadata);
if (metadata.foundSiblingSelector) if (metadata.foundSiblingSelector)
...@@ -478,17 +443,11 @@ DescendantInvalidationSet& RuleFeatureSet::ensurePseudoInvalidationSet(CSSSelect ...@@ -478,17 +443,11 @@ DescendantInvalidationSet& RuleFeatureSet::ensurePseudoInvalidationSet(CSSSelect
return *addResult.storedValue->value; return *addResult.storedValue->value;
} }
void RuleFeatureSet::collectFeaturesFromSelector(const CSSSelector& selector, RuleFeatureSet::FeatureMetadata& metadata, InvalidationSetMode mode) void RuleFeatureSet::collectFeaturesFromSelector(const CSSSelector& selector, RuleFeatureSet::FeatureMetadata& metadata)
{ {
unsigned maxDirectAdjacentSelectors = 0; unsigned maxDirectAdjacentSelectors = 0;
for (const CSSSelector* current = &selector; current; current = current->tagHistory()) { for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
if (mode != AddFeatures) {
if (DescendantInvalidationSet* invalidationSet = invalidationSetForSelector(*current)) {
if (mode == UseSubtreeStyleChange)
invalidationSet->setWholeSubtreeInvalid();
}
}
if (current->pseudoType() == CSSSelector::PseudoFirstLine) if (current->pseudoType() == CSSSelector::PseudoFirstLine)
metadata.usesFirstLineRules = true; metadata.usesFirstLineRules = true;
if (current->isDirectAdjacentSelector()) { if (current->isDirectAdjacentSelector()) {
...@@ -501,24 +460,17 @@ void RuleFeatureSet::collectFeaturesFromSelector(const CSSSelector& selector, Ru ...@@ -501,24 +460,17 @@ void RuleFeatureSet::collectFeaturesFromSelector(const CSSSelector& selector, Ru
if (current->isSiblingSelector()) if (current->isSiblingSelector())
metadata.foundSiblingSelector = true; metadata.foundSiblingSelector = true;
collectFeaturesFromSelectorList(current->selectorList(), metadata, mode); const CSSSelectorList* selectorList = current->selectorList();
if (!selectorList)
continue;
if (mode == UseLocalStyleChange && current->relation() != CSSSelector::SubSelector) for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(*selector))
mode = UseSubtreeStyleChange; collectFeaturesFromSelector(*selector, metadata);
} }
ASSERT(!maxDirectAdjacentSelectors); ASSERT(!maxDirectAdjacentSelectors);
} }
void RuleFeatureSet::collectFeaturesFromSelectorList(const CSSSelectorList* selectorList, RuleFeatureSet::FeatureMetadata& metadata, InvalidationSetMode mode)
{
if (!selectorList)
return;
for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(*selector))
collectFeaturesFromSelector(*selector, metadata, mode);
}
void RuleFeatureSet::FeatureMetadata::add(const FeatureMetadata& other) void RuleFeatureSet::FeatureMetadata::add(const FeatureMetadata& other)
{ {
usesFirstLineRules = usesFirstLineRules || other.usesFirstLineRules; usesFirstLineRules = usesFirstLineRules || other.usesFirstLineRules;
......
...@@ -125,23 +125,14 @@ private: ...@@ -125,23 +125,14 @@ private:
unsigned maxDirectAdjacentSelectors; unsigned maxDirectAdjacentSelectors;
}; };
enum InvalidationSetMode { void collectFeaturesFromSelector(const CSSSelector&, FeatureMetadata&);
AddFeatures,
UseLocalStyleChange,
UseSubtreeStyleChange
};
static InvalidationSetMode invalidationSetModeForSelector(const CSSSelector&);
void collectFeaturesFromSelector(const CSSSelector&, FeatureMetadata&, InvalidationSetMode);
void collectFeaturesFromSelectorList(const CSSSelectorList*, FeatureMetadata&, InvalidationSetMode);
DescendantInvalidationSet& ensureClassInvalidationSet(const AtomicString& className); DescendantInvalidationSet& ensureClassInvalidationSet(const AtomicString& className);
DescendantInvalidationSet& ensureAttributeInvalidationSet(const AtomicString& attributeName); DescendantInvalidationSet& ensureAttributeInvalidationSet(const AtomicString& attributeName);
DescendantInvalidationSet& ensureIdInvalidationSet(const AtomicString& attributeName); DescendantInvalidationSet& ensureIdInvalidationSet(const AtomicString& attributeName);
DescendantInvalidationSet& ensurePseudoInvalidationSet(CSSSelector::PseudoType); DescendantInvalidationSet& ensurePseudoInvalidationSet(CSSSelector::PseudoType);
InvalidationSetMode updateInvalidationSets(const CSSSelector&); void updateInvalidationSets(const CSSSelector&);
struct InvalidationSetFeatures { struct InvalidationSetFeatures {
InvalidationSetFeatures() InvalidationSetFeatures()
...@@ -149,7 +140,11 @@ private: ...@@ -149,7 +140,11 @@ private:
, treeBoundaryCrossing(false) , treeBoundaryCrossing(false)
, adjacent(false) , adjacent(false)
, insertionPointCrossing(false) , insertionPointCrossing(false)
, forceSubtree(false)
{ } { }
bool useSubtreeInvalidation() const { return forceSubtree || adjacent; }
Vector<AtomicString> classes; Vector<AtomicString> classes;
Vector<AtomicString> attributes; Vector<AtomicString> attributes;
AtomicString id; AtomicString id;
...@@ -158,10 +153,14 @@ private: ...@@ -158,10 +153,14 @@ private:
bool treeBoundaryCrossing; bool treeBoundaryCrossing;
bool adjacent; bool adjacent;
bool insertionPointCrossing; bool insertionPointCrossing;
bool forceSubtree;
}; };
static void extractInvalidationSetFeature(const CSSSelector&, InvalidationSetFeatures&); static bool extractInvalidationSetFeature(const CSSSelector&, InvalidationSetFeatures&);
const CSSSelector* extractInvalidationSetFeatures(const CSSSelector&, InvalidationSetFeatures&, bool negated);
enum UseFeaturesType { UseFeatures, ForceSubtree };
std::pair<const CSSSelector*, UseFeaturesType> extractInvalidationSetFeatures(const CSSSelector&, InvalidationSetFeatures&, bool negated);
void addFeaturesToInvalidationSet(DescendantInvalidationSet&, const InvalidationSetFeatures&); void addFeaturesToInvalidationSet(DescendantInvalidationSet&, const InvalidationSetFeatures&);
void addFeaturesToInvalidationSets(const CSSSelector&, InvalidationSetFeatures&); void addFeaturesToInvalidationSets(const CSSSelector&, InvalidationSetFeatures&);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment