bool StyleInvalidator::invalidate(Element& element, RecursionData& recursionData, SiblingData& siblingData) { siblingData.advance(); RecursionCheckpoint checkpoint(&recursionData); bool thisElementNeedsStyleRecalc = checkInvalidationSetsAgainstElement(element, recursionData, siblingData); bool someChildrenNeedStyleRecalc = false; if (recursionData.hasInvalidationSets() || element.childNeedsStyleInvalidation()) someChildrenNeedStyleRecalc = invalidateChildren(element, recursionData); if (thisElementNeedsStyleRecalc) { ASSERT(!recursionData.wholeSubtreeInvalid()); element.setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); } else if (recursionData.hasInvalidationSets() && someChildrenNeedStyleRecalc) { // Clone the ComputedStyle in order to preserve correct style sharing, if possible. Otherwise recalc style. if (LayoutObject* layoutObject = element.layoutObject()) { layoutObject->setStyleInternal(ComputedStyle::clone(layoutObject->styleRef())); } else { TRACE_STYLE_INVALIDATOR_INVALIDATION_IF_ENABLED(element, PreventStyleSharingForParent); element.setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); } } if (recursionData.insertionPointCrossing() && element.isInsertionPoint()) element.setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); element.clearChildNeedsStyleInvalidation(); element.clearNeedsStyleInvalidation(); return thisElementNeedsStyleRecalc; }
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] only applies to earlier siblings. Remove it. m_invalidationEntries[index] = m_invalidationEntries.last(); m_invalidationEntries.removeLast(); continue; } const SiblingInvalidationSet& invalidationSet = *m_invalidationEntries[index].m_invalidationSet; ++index; if (!invalidationSet.invalidatesElement(element)) continue; if (invalidationSet.invalidatesSelf()) thisElementNeedsStyleRecalc = true; if (const DescendantInvalidationSet* descendants = invalidationSet.siblingDescendants()) { if (descendants->wholeSubtreeInvalid()) { element.setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); return true; } if (!descendants->isEmpty()) recursionData.pushInvalidationSet(*descendants); } } return thisElementNeedsStyleRecalc; }
ALWAYS_INLINE bool StyleInvalidator::checkInvalidationSetsAgainstElement(Element& element, RecursionData& recursionData, SiblingData& siblingData) { if (element.styleChangeType() >= SubtreeStyleChange || recursionData.wholeSubtreeInvalid()) { recursionData.setWholeSubtreeInvalid(); return false; } bool thisElementNeedsStyleRecalc = recursionData.matchesCurrentInvalidationSets(element); if (UNLIKELY(!siblingData.isEmpty())) thisElementNeedsStyleRecalc |= siblingData.matchCurrentInvalidationSets(element, recursionData); if (UNLIKELY(element.needsStyleInvalidation())) pushInvalidationSetsForElement(element, recursionData, siblingData); return thisElementNeedsStyleRecalc; }
void StyleInvalidator::invalidateSlotDistributedElements(HTMLSlotElement& slot, const RecursionData& recursionData) const { for (auto& distributedNode : slot.getDistributedNodes()) { if (distributedNode->needsStyleRecalc()) continue; if (!distributedNode->isElementNode()) continue; if (recursionData.matchesCurrentInvalidationSetsAsSlotted(toElement(*distributedNode))) distributedNode->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); } }
bool StyleInvalidator::SiblingData::matchCurrentInvalidationSets(Element& element, RecursionData& recursionData) const { 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] only applies to earlier siblings. Remove it. m_invalidationEntries[index] = m_invalidationEntries.last(); m_invalidationEntries.removeLast(); continue; } const SiblingInvalidationSet& invalidationSet = *m_invalidationEntries[index].m_invalidationSet; if (invalidationSet.invalidatesElement(element)) { const DescendantInvalidationSet& descendants = invalidationSet.descendants(); if (descendants.wholeSubtreeInvalid()) { // Avoid directly setting SubtreeStyleChange on element, or ContainerNode::checkForChildrenAdjacentRuleChanges() // may propagate the SubtreeStyleChange to our own siblings' subtrees. for (Element* child = ElementTraversal::firstChild(element); child; child = ElementTraversal::nextSibling(*child)) { child->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector)); } return true; } if (descendants.invalidatesSelf()) thisElementNeedsStyleRecalc = true; if (!descendants.isEmpty()) recursionData.pushInvalidationSet(descendants); } ++index; } return thisElementNeedsStyleRecalc; }
bool StyleInvalidator::invalidateShadowRootChildren(Element& element, RecursionData& recursionData) { bool someChildrenNeedStyleRecalc = false; for (ShadowRoot* root = element.youngestShadowRoot(); root; root = root->olderShadowRoot()) { if (!recursionData.treeBoundaryCrossing() && !root->childNeedsStyleInvalidation() && !root->needsStyleInvalidation()) continue; SiblingData siblingData; for (Element* child = ElementTraversal::firstChild(*root); child; child = ElementTraversal::nextSibling(*child)) { bool childRecalced = invalidate(*child, recursionData, siblingData); someChildrenNeedStyleRecalc = someChildrenNeedStyleRecalc || childRecalced; } root->clearChildNeedsStyleInvalidation(); root->clearNeedsStyleInvalidation(); } return someChildrenNeedStyleRecalc; }
void StyleInvalidator::pushInvalidationSetsForElement(Element& element, RecursionData& recursionData, SiblingData& siblingData) { PendingInvalidations* pendingInvalidations = m_pendingInvalidationMap.get(&element); ASSERT(pendingInvalidations); for (const auto& invalidationSet : pendingInvalidations->siblings()) siblingData.pushInvalidationSet(toSiblingInvalidationSet(*invalidationSet)); if (!pendingInvalidations->descendants().isEmpty()) { for (const auto& invalidationSet : pendingInvalidations->descendants()) recursionData.pushInvalidationSet(toDescendantInvalidationSet(*invalidationSet)); if (UNLIKELY(*s_tracingEnabled)) { TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.invalidationTracking"), "StyleInvalidatorInvalidationTracking", TRACE_EVENT_SCOPE_THREAD, "data", InspectorStyleInvalidatorInvalidateEvent::invalidationList(element, pendingInvalidations->descendants())); } } }
void StyleInvalidator::pushInvalidationSetsForContainerNode(ContainerNode& node, RecursionData& recursionData, SiblingData& siblingData) { PendingInvalidations* pendingInvalidations = m_pendingInvalidationMap.get(&node); ASSERT(pendingInvalidations); for (const auto& invalidationSet : pendingInvalidations->siblings()) siblingData.pushInvalidationSet(toSiblingInvalidationSet(*invalidationSet)); if (node.getStyleChangeType() >= SubtreeStyleChange) return; if (!pendingInvalidations->descendants().isEmpty()) { for (const auto& invalidationSet : pendingInvalidations->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(node, pendingInvalidations->descendants())); } } }