Index: Source/core/css/RuleFeature.cpp |
diff --git a/Source/core/css/RuleFeature.cpp b/Source/core/css/RuleFeature.cpp |
index 8dfb8cdac6adf67b7ea6c45c4c51d41b4e9311b2..9f337db19e92ff2bbabe0d8b82478a91aee50312 100644 |
--- a/Source/core/css/RuleFeature.cpp |
+++ b/Source/core/css/RuleFeature.cpp |
@@ -34,6 +34,12 @@ |
#include "core/css/CSSSelector.h" |
#include "core/css/CSSSelectorList.h" |
#include "core/css/RuleSet.h" |
+#include "core/dom/Document.h" |
+#include "core/dom/Element.h" |
+#include "core/dom/Node.h" |
+#include "core/dom/shadow/ElementShadow.h" |
+#include "core/dom/shadow/ShadowRoot.h" |
+#include "wtf/BitVector.h" |
namespace WebCore { |
@@ -55,9 +61,8 @@ static bool supportsClassDescendantInvalidation(const CSSSelector* selector) |
// FIXME: We should allow pseudo elements, but we need to change how they hook |
// into recalcStyle by moving them to recalcOwnStyle instead of recalcChildStyle. |
- if (component->m_match == CSSSelector::Tag |
- || component->m_match == CSSSelector::Id |
- || component->m_match == CSSSelector::Class) { |
+ // FIXME: next up: Tag and Id. |
+ if (component->m_match == CSSSelector::Class) { |
if (!foundDescendantRelation) |
foundIdent = true; |
else |
@@ -240,4 +245,175 @@ void RuleFeatureSet::clear() |
uncommonAttributeRules.clear(); |
} |
+void RuleFeatureSet::scheduleStyleInvalidationForClassChange(const SpaceSplitString& changedClasses, Element* element) |
+{ |
+ if (scheduleStyleInvalidationForClassChangeInternal(changedClasses, element) |
+ && !RuntimeEnabledFeatures::targetedStyleRecalcEnabled()) |
+ element->setNeedsStyleRecalc(); |
+} |
+ |
+void RuleFeatureSet::scheduleStyleInvalidationForClassChange(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses, Element* element) |
+{ |
+ if (scheduleStyleInvalidationForClassChangeInternal(oldClasses, newClasses, element) |
+ && !RuntimeEnabledFeatures::targetedStyleRecalcEnabled()) |
+ element->setNeedsStyleRecalc(); |
+} |
+ |
+bool RuleFeatureSet::scheduleStyleInvalidationForClassChangeInternal(const SpaceSplitString& changedClasses, Element* element) |
+{ |
+ unsigned changedSize = changedClasses.size(); |
+ for (unsigned i = 0; i < changedSize; ++i) { |
+ if (RuntimeEnabledFeatures::targetedStyleRecalcEnabled()) |
+ addClassToInvalidationSet(changedClasses[i], element); |
+ else if (m_metadata.classesInRules.contains(changedClasses[i])) |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+bool RuleFeatureSet::scheduleStyleInvalidationForClassChangeInternal(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses, Element* element) |
+{ |
+ if (!oldClasses.size()) |
+ return scheduleStyleInvalidationForClassChangeInternal(newClasses, element); |
+ |
+ // Class vectors tend to be very short. This is faster than using a hash table. |
+ BitVector remainingClassBits; |
+ remainingClassBits.ensureSize(oldClasses.size()); |
+ |
+ for (unsigned i = 0; i < newClasses.size(); ++i) { |
+ bool found = false; |
+ for (unsigned j = 0; j < oldClasses.size(); ++j) { |
+ if (newClasses[i] == oldClasses[j]) { |
+ // Mark each class that is still in the newClasses so we can skip doing |
+ // an n^2 search below when looking for removals. We can't break from |
+ // this loop early since a class can appear more than once. |
+ remainingClassBits.quickSet(j); |
+ found = true; |
+ } |
+ } |
+ // Class was added. |
+ if (!found) { |
+ if (RuntimeEnabledFeatures::targetedStyleRecalcEnabled()) |
+ addClassToInvalidationSet(newClasses[i], element); |
+ else if (m_metadata.classesInRules.contains(newClasses[i])) |
+ return true; |
+ } |
+ } |
+ |
+ for (unsigned i = 0; i < oldClasses.size(); ++i) { |
+ if (remainingClassBits.quickGet(i)) |
+ continue; |
+ |
+ // Class was removed. |
+ if (RuntimeEnabledFeatures::targetedStyleRecalcEnabled()) |
+ addClassToInvalidationSet(oldClasses[i], element); |
+ else if (m_metadata.classesInRules.contains(oldClasses[i])) |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+void RuleFeatureSet::addClassToInvalidationSet(const AtomicString& className, Element* element) |
+{ |
+ if (DescendantInvalidationSet* invalidationSet = m_classInvalidationSets.get(className)) { |
+ ensurePendingInvalidationList(element).append(invalidationSet); |
+ element->setNeedsStyleInvalidation(); |
+ } |
+} |
+ |
+RuleFeatureSet::InvalidationList& RuleFeatureSet::ensurePendingInvalidationList(Element* element) |
+{ |
+ PendingInvalidationMap::AddResult addResult = m_pendingInvalidationMap.add(element, 0); |
+ if (addResult.isNewEntry) |
+ addResult.iterator->value = new InvalidationList; |
+ return *addResult.iterator->value; |
+} |
+ |
+void RuleFeatureSet::computeStyleInvalidation(Document& document) |
+{ |
+ Vector<AtomicString> invalidationClasses; |
+ for (Node* child = document.firstChild(); child; child = child->nextSibling()) { |
+ if (child->isElementNode() && child->childNeedsStyleInvalidation()) { |
+ Element* childElement = toElement(child); |
+ computeStyleInvalidationInternal(childElement, invalidationClasses, false); |
+ } |
+ } |
+ document.clearChildNeedsStyleInvalidation(); |
+ m_pendingInvalidationMap.clear(); |
+} |
+ |
+bool RuleFeatureSet::computeStyleInvalidationForChildren(Element* element, Vector<AtomicString>& invalidationClasses, bool foundInvalidationSet) |
+{ |
+ bool someChildrenNeedStyleRecalc = false; |
+ for (Node* child = element->firstChild(); child; child = child->nextSibling()) { |
+ if (child->isElementNode()) { |
+ Element* childElement = toElement(child); |
+ bool childRecalced = computeStyleInvalidationInternal(childElement, invalidationClasses, foundInvalidationSet); |
+ someChildrenNeedStyleRecalc = someChildrenNeedStyleRecalc || childRecalced; |
+ } |
+ } |
+ for (ShadowRoot* root = element->youngestShadowRoot(); root; root = root->olderShadowRoot()) { |
+ for (Node* child = root->firstChild(); child; child = child->nextSibling()) { |
+ if (child->isElementNode()) { |
+ Element* childElement = toElement(child); |
+ bool childRecalced = computeStyleInvalidationInternal(childElement, invalidationClasses, foundInvalidationSet); |
+ someChildrenNeedStyleRecalc = someChildrenNeedStyleRecalc || childRecalced; |
+ } |
+ } |
+ } |
+ return someChildrenNeedStyleRecalc; |
+} |
+ |
+bool RuleFeatureSet::computeStyleInvalidationInternal(Element* element, Vector<AtomicString>& invalidationClasses, bool foundInvalidationSet) |
+{ |
+ int oldSize = invalidationClasses.size(); |
+ if (element->needsStyleInvalidation()) { |
+ if (InvalidationList* invalidationList = m_pendingInvalidationMap.get(element)) { |
+ foundInvalidationSet = true; |
+ bool recalcWholeSubtree = false; |
+ for (InvalidationList::const_iterator it = invalidationList->begin(); it != invalidationList->end(); ++it) { |
+ if ((*it)->wholeSubtreeInvalid()) { |
+ recalcWholeSubtree = true; |
+ break; |
+ } |
+ (*it)->getClasses(invalidationClasses); |
+ } |
+ if (recalcWholeSubtree) { |
+ element->setNeedsStyleRecalc(SubtreeStyleChange); |
+ invalidationClasses.remove(oldSize, invalidationClasses.size() - oldSize); |
+ element->clearChildNeedsStyleInvalidation(); |
+ return true; |
+ } |
+ } |
+ } |
+ |
+ bool thisElementNeedsStyleRecalc = false; |
+ |
+ if (element->hasClass()) { |
+ const SpaceSplitString& classNames = element->classNames(); |
+ for (Vector<AtomicString>::const_iterator it = invalidationClasses.begin(); it != invalidationClasses.end(); ++it) { |
+ if (classNames.contains(*it)) { |
+ thisElementNeedsStyleRecalc = true; |
+ break; |
+ } |
+ } |
+ } |
+ |
+ // foundInvalidationSet will be true if we are in a subtree of a node with a DescendantInvalidationSet on it. |
+ // We need to check all nodes in the subtree of such a node. |
+ if (foundInvalidationSet || element->childNeedsStyleInvalidation()) { |
+ bool someChildrenNeedStyleRecalc = computeStyleInvalidationForChildren(element, invalidationClasses, foundInvalidationSet); |
+ // We only need to possibly recalc style if this node is in the subtree of a node with a DescendantInvalidationSet on it. |
+ if (foundInvalidationSet) |
+ thisElementNeedsStyleRecalc = thisElementNeedsStyleRecalc || someChildrenNeedStyleRecalc; |
+ } |
+ |
+ if (thisElementNeedsStyleRecalc) |
+ element->setNeedsStyleRecalc(LocalStyleChange); |
+ |
+ invalidationClasses.remove(oldSize, invalidationClasses.size() - oldSize); |
+ element->clearChildNeedsStyleInvalidation(); |
+ return thisElementNeedsStyleRecalc; |
+} |
+ |
} // namespace WebCore |