Chromium Code Reviews| Index: Source/core/css/invalidation/StyleInvalidator.cpp |
| diff --git a/Source/core/css/invalidation/StyleInvalidator.cpp b/Source/core/css/invalidation/StyleInvalidator.cpp |
| index 517a78403201a0eaa2aec12b3f7efd0916fb8b23..720cd0dad815fe1361a36d409fdb050d0200c103 100644 |
| --- a/Source/core/css/invalidation/StyleInvalidator.cpp |
| +++ b/Source/core/css/invalidation/StyleInvalidator.cpp |
| @@ -33,39 +33,54 @@ static const unsigned char* s_tracingEnabled = nullptr; |
| void StyleInvalidator::invalidate(Document& document) |
| { |
| RecursionData recursionData; |
| + SiblingData siblingData; |
| if (Element* documentElement = document.documentElement()) |
| - invalidate(*documentElement, recursionData); |
| + invalidate(*documentElement, recursionData, siblingData); |
| document.clearChildNeedsStyleInvalidation(); |
| document.clearNeedsStyleInvalidation(); |
| clearPendingInvalidations(); |
| } |
| -void StyleInvalidator::scheduleInvalidation(PassRefPtrWillBeRawPtr<InvalidationSet> invalidationSet, Element& element) |
| +void StyleInvalidator::scheduleInvalidationSetsForElement(const InvalidationLists& invalidationLists, Element& element) |
| { |
| ASSERT(element.inActiveDocument()); |
| if (element.styleChangeType() >= SubtreeStyleChange) |
| return; |
| - if (invalidationSet->wholeSubtreeInvalid()) { |
| - element.setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); |
| - clearInvalidation(element); |
| - return; |
| + const InvalidationSetVector& descendant = invalidationLists.descendants(); |
| + const InvalidationSetVector& sibling = invalidationLists.siblings(); |
| + |
| + bool requiresDescendantInvalidation = false; |
| + |
| + for (auto& invalidationSet : descendant) { |
| + if (invalidationSet->wholeSubtreeInvalid()) { |
| + element.setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); |
| + return; |
| + } |
| + |
| + ASSERT(invalidationSet->appliesDirectly() || !invalidationSet->isEmpty()); |
| + if (invalidationSet->appliesDirectly()) |
| + element.setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); |
| + if (!invalidationSet->isEmpty()) |
| + requiresDescendantInvalidation = true; |
| } |
| - if (invalidationSet->isEmpty()) { |
| - element.setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); |
| + |
| + if (sibling.isEmpty() && !requiresDescendantInvalidation) |
| return; |
| - } |
| - InvalidationList& list = ensurePendingInvalidationList(element); |
| - list.append(invalidationSet); |
| element.setNeedsStyleInvalidation(); |
| -} |
| + InvalidationLists& listData = ensurePendingInvalidationLists(element); |
| -StyleInvalidator::InvalidationList& StyleInvalidator::ensurePendingInvalidationList(Element& element) |
| -{ |
| - PendingInvalidationMap::AddResult addResult = m_pendingInvalidationMap.add(&element, nullptr); |
| - if (addResult.isNewEntry) |
| - addResult.storedValue->value = adoptPtrWillBeNoop(new InvalidationList); |
| - return *addResult.storedValue->value; |
| + for (auto& invalidationSet : sibling) |
| + listData.siblings().append(invalidationSet); |
| + |
| + if (!requiresDescendantInvalidation) |
| + return; |
| + |
| + for (auto& invalidationSet : descendant) { |
| + ASSERT(!invalidationSet->wholeSubtreeInvalid()); |
| + if (!invalidationSet->isEmpty()) |
| + listData.descendants().append(invalidationSet); |
| + } |
| } |
| void StyleInvalidator::clearInvalidation(Element& element) |
| @@ -76,6 +91,14 @@ void StyleInvalidator::clearInvalidation(Element& element) |
| element.clearNeedsStyleInvalidation(); |
| } |
| +InvalidationLists& StyleInvalidator::ensurePendingInvalidationLists(Element& element) |
| +{ |
| + PendingInvalidationMap::AddResult addResult = m_pendingInvalidationMap.add(&element, nullptr); |
| + if (addResult.isNewEntry) |
| + addResult.storedValue->value = adoptPtr(new InvalidationLists); |
| + return *addResult.storedValue->value; |
| +} |
| + |
| void StyleInvalidator::clearPendingInvalidations() |
| { |
| m_pendingInvalidationMap.clear(); |
| @@ -104,7 +127,7 @@ void StyleInvalidator::RecursionData::pushInvalidationSet(const InvalidationSet& |
| m_invalidateCustomPseudo = invalidationSet.customPseudoInvalid(); |
| } |
| -ALWAYS_INLINE bool StyleInvalidator::RecursionData::matchesCurrentInvalidationSets(Element& element) |
| +ALWAYS_INLINE bool StyleInvalidator::RecursionData::matchesCurrentInvalidationSets(Element& element) const |
| { |
| if (m_invalidateCustomPseudo && element.shadowPseudoId() != nullAtom) { |
| TRACE_STYLE_INVALIDATOR_INVALIDATION_IF_ENABLED(element, InvalidateCustomPseudo); |
| @@ -122,54 +145,115 @@ ALWAYS_INLINE bool StyleInvalidator::RecursionData::matchesCurrentInvalidationSe |
| return false; |
| } |
| -ALWAYS_INLINE bool StyleInvalidator::checkInvalidationSetsAgainstElement(Element& element, StyleInvalidator::RecursionData& recursionData) |
| +void StyleInvalidator::SiblingData::pushInvalidationSet(const InvalidationSet& invalidationSet) |
| +{ |
| + unsigned invalidationLimit; |
| + if (invalidationSet.maxDirectAdjacentSelectors() == std::numeric_limits<unsigned>::max()) |
| + invalidationLimit = std::numeric_limits<unsigned>::max(); |
| + else |
| + invalidationLimit = m_elementIndex + invalidationSet.maxDirectAdjacentSelectors(); |
| + m_invalidationEntries.append(Entry(&invalidationSet, invalidationLimit)); |
| +} |
| + |
| +ALWAYS_INLINE bool StyleInvalidator::SiblingData::matchCurrentInvalidationSets(Element& element, RecursionData& recursionData) |
| +{ |
| + bool thisElementNeedsStyleRecalc = false; |
| + ASSERT(!recursionData.wholeSubtreeInvalid()); |
| + |
| + unsigned index = 0; |
| + while (index < m_invalidationEntries.size()) { |
| + if (m_elementIndex > m_invalidationEntries[index].m_invalidationLimit) { |
| + m_invalidationEntries[index] = m_invalidationEntries.last(); |
| + m_invalidationEntries.removeLast(); |
| + continue; |
| + } |
| + |
| + const InvalidationSet* invalidationSet = m_invalidationEntries[index].m_invalidationSet.get(); |
| + |
| + if (invalidationSet->invalidatesElement(element)) { |
| + ASSERT(invalidationSet->appliesDirectly() || invalidationSet->descendants()); |
| + if (invalidationSet->appliesDirectly()) |
| + thisElementNeedsStyleRecalc = true; |
| + if (const InvalidationSet* descendants = invalidationSet->descendants()) { |
| + if (descendants->wholeSubtreeInvalid()) { |
| + // Avoid directly setting SubtreeStyleChange on element, or ContainerNode::checkForChildrenAdjacentRuleChanges() |
| + // may propagate the SubtreeStyleChange to our own siblings' subtrees. |
|
rune
2015/09/14 23:06:51
I think that we ultimately want to end up with rem
Eric Willigers
2015/09/15 05:39:18
Yes, the complication is DOM changes, e.g. removin
|
| + |
| + for (Element* child = ElementTraversal::firstChild(element); child; child = ElementTraversal::nextSibling(*child)) { |
| + child->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector)); |
| + } |
| + return true; |
| + } |
| + |
| + recursionData.pushInvalidationSet(*descendants); |
| + } |
| + } |
| + |
| + ++index; |
| + } |
| + return thisElementNeedsStyleRecalc; |
| +} |
| + |
| +ALWAYS_INLINE bool StyleInvalidator::checkInvalidationSetsAgainstElement(Element& element, RecursionData& recursionData, SiblingData& siblingData) |
| { |
| if (element.styleChangeType() >= SubtreeStyleChange || recursionData.wholeSubtreeInvalid()) { |
| recursionData.setWholeSubtreeInvalid(); |
| return false; |
| } |
| - if (element.needsStyleInvalidation()) { |
| - if (InvalidationList* invalidationList = m_pendingInvalidationMap.get(&element)) { |
| - for (const auto& invalidationSet : *invalidationList) |
| + |
| + bool thisElementNeedsStyleRecalc = recursionData.matchesCurrentInvalidationSets(element); |
| + thisElementNeedsStyleRecalc |= siblingData.matchCurrentInvalidationSets(element, recursionData); |
| + |
| + if (UNLIKELY(element.needsStyleInvalidation())) { |
| + InvalidationLists* listData = m_pendingInvalidationMap.get(&element); |
| + ASSERT(listData); |
| + |
| + if (!listData->descendants().isEmpty()) { |
| + for (const auto& invalidationSet : listData->descendants()) |
| recursionData.pushInvalidationSet(*invalidationSet); |
| if (UNLIKELY(*s_tracingEnabled)) { |
| TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.invalidationTracking"), |
| "StyleInvalidatorInvalidationTracking", |
| TRACE_EVENT_SCOPE_THREAD, |
| - "data", InspectorStyleInvalidatorInvalidateEvent::invalidationList(element, *invalidationList)); |
| + "data", InspectorStyleInvalidatorInvalidateEvent::invalidationList(element, listData->descendants())); |
| } |
| - return true; |
| } |
| + |
| + for (const auto& invalidationSet : listData->siblings()) |
| + siblingData.pushInvalidationSet(*invalidationSet); |
| } |
| - return recursionData.matchesCurrentInvalidationSets(element); |
| + return thisElementNeedsStyleRecalc; |
| } |
| -bool StyleInvalidator::invalidateChildren(Element& element, StyleInvalidator::RecursionData& recursionData) |
| +bool StyleInvalidator::invalidateChildren(Element& element, RecursionData& recursionData) |
| { |
| + SiblingData siblingData; |
| + |
| bool someChildrenNeedStyleRecalc = false; |
| for (ShadowRoot* root = element.youngestShadowRoot(); root; root = root->olderShadowRoot()) { |
| if (!recursionData.treeBoundaryCrossing() && !root->childNeedsStyleInvalidation() && !root->needsStyleInvalidation()) |
| continue; |
| for (Element* child = ElementTraversal::firstChild(*root); child; child = ElementTraversal::nextSibling(*child)) { |
| - bool childRecalced = invalidate(*child, recursionData); |
| + bool childRecalced = invalidate(*child, recursionData, siblingData); |
| someChildrenNeedStyleRecalc = someChildrenNeedStyleRecalc || childRecalced; |
| } |
| root->clearChildNeedsStyleInvalidation(); |
| root->clearNeedsStyleInvalidation(); |
| } |
| for (Element* child = ElementTraversal::firstChild(element); child; child = ElementTraversal::nextSibling(*child)) { |
| - bool childRecalced = invalidate(*child, recursionData); |
| + bool childRecalced = invalidate(*child, recursionData, siblingData); |
| someChildrenNeedStyleRecalc = someChildrenNeedStyleRecalc || childRecalced; |
| } |
| return someChildrenNeedStyleRecalc; |
| } |
| -bool StyleInvalidator::invalidate(Element& element, StyleInvalidator::RecursionData& recursionData) |
| +bool StyleInvalidator::invalidate(Element& element, RecursionData& recursionData, SiblingData& siblingData) |
| { |
| RecursionCheckpoint checkpoint(&recursionData); |
| + siblingData.advance(); |
| - bool thisElementNeedsStyleRecalc = checkInvalidationSetsAgainstElement(element, recursionData); |
| + bool thisElementNeedsStyleRecalc = checkInvalidationSetsAgainstElement(element, recursionData, siblingData); |
| bool someChildrenNeedStyleRecalc = false; |
| if (recursionData.hasInvalidationSets() || element.childNeedsStyleInvalidation()) |