void RenderTreeUpdater::updateElementRenderer(Element& element, const Style::ElementUpdate& update) { #if PLATFORM(IOS) CheckForVisibilityChange checkForVisibilityChange(element); #endif bool shouldTearDownRenderers = update.change == Style::Detach && (element.renderer() || element.isNamedFlowContentElement()); if (shouldTearDownRenderers) tearDownRenderers(element, TeardownType::KeepHoverAndActive); bool hasDisplayContents = update.style->display() == CONTENTS; if (hasDisplayContents != element.hasDisplayContents()) { element.setHasDisplayContents(hasDisplayContents); // Render tree position needs to be recomputed as rendering siblings may be found from the display:contents subtree. renderTreePosition().invalidateNextSibling(); } bool shouldCreateNewRenderer = !element.renderer() && !hasDisplayContents; if (shouldCreateNewRenderer) { if (element.hasCustomStyleResolveCallbacks()) element.willAttachRenderers(); createRenderer(element, RenderStyle::clone(*update.style)); invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(element); return; } if (!element.renderer()) return; auto& renderer = *element.renderer(); if (update.recompositeLayer) { renderer.setStyle(RenderStyle::clone(*update.style), StyleDifferenceRecompositeLayer); return; } if (update.change == Style::NoChange) { if (pseudoStyleCacheIsInvalid(&renderer, update.style.get())) { renderer.setStyle(RenderStyle::clone(*update.style), StyleDifferenceEqual); return; } return; } renderer.setStyle(RenderStyle::clone(*update.style), StyleDifferenceEqual); }
void TreeResolver::resolveComposedTree() { ASSERT(m_parentStack.size() == 1); ASSERT(m_scopeStack.size() == 1); auto descendants = composedTreeDescendants(m_document); auto it = descendants.begin(); auto end = descendants.end(); // FIXME: SVG <use> element may cause tree mutations during style recalc. it.dropAssertions(); while (it != end) { popParentsToDepth(it.depth()); auto& node = *it; auto& parent = this->parent(); ASSERT(node.containingShadowRoot() == scope().shadowRoot); ASSERT(node.parentElement() == parent.element || is<ShadowRoot>(node.parentNode()) || node.parentElement()->shadowRoot()); if (is<Text>(node)) { auto& text = downcast<Text>(node); if (text.styleChangeType() == ReconstructRenderTree && parent.change != Detach) m_update->addText(text, parent.element); text.clearNeedsStyleRecalc(); it.traverseNextSkippingChildren(); continue; } auto& element = downcast<Element>(node); if (it.depth() > Settings::defaultMaximumRenderTreeDepth) { resetStyleForNonRenderedDescendants(element); element.clearChildNeedsStyleRecalc(); it.traverseNextSkippingChildren(); continue; } // FIXME: We should deal with this during style invalidation. bool affectedByPreviousSibling = element.styleIsAffectedByPreviousSibling() && parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle; if (element.needsStyleRecalc() || parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle) parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle = element.affectsNextSiblingElementStyle(); bool shouldResolveForPseudoElement = shouldResolvePseudoElement(element.beforePseudoElement()) || shouldResolvePseudoElement(element.afterPseudoElement()); const RenderStyle* style; Change change; bool shouldResolve = parent.change >= Inherit || element.needsStyleRecalc() || shouldResolveForPseudoElement || affectedByPreviousSibling || element.hasDisplayContents(); if (shouldResolve) { #if PLATFORM(IOS) CheckForVisibilityChangeOnRecalcStyle checkForVisibilityChange(&element, element.renderStyle()); #endif element.resetComputedStyle(); if (element.hasCustomStyleResolveCallbacks()) { if (!element.willRecalcStyle(parent.change)) { it.traverseNextSkippingChildren(); continue; } } auto elementUpdate = resolveElement(element); if (element.hasCustomStyleResolveCallbacks()) element.didRecalcStyle(elementUpdate.change); style = elementUpdate.style.get(); change = elementUpdate.change; if (affectedByPreviousSibling && change != Detach) change = Force; if (elementUpdate.style) m_update->addElement(element, parent.element, WTFMove(elementUpdate)); element.clearNeedsStyleRecalc(); } else { style = element.renderStyle(); change = NoChange; } if (!style) { resetStyleForNonRenderedDescendants(element); element.clearChildNeedsStyleRecalc(); } bool shouldIterateChildren = style && (element.childNeedsStyleRecalc() || change != NoChange); if (!shouldIterateChildren) { it.traverseNextSkippingChildren(); continue; } pushParent(element, *style, change); it.traverseNext(); } popParentsToDepth(1); }
void resolveTree(Element& current, Change change) { ASSERT(change != Detach); if (current.hasCustomStyleResolveCallbacks()) { if (!current.willRecalcStyle(change)) return; } ContainerNode* renderingParentNode = NodeRenderingTraversal::parent(¤t); bool hasParentStyle = renderingParentNode && renderingParentNode->renderStyle(); bool hasDirectAdjacentRules = current.childrenAffectedByDirectAdjacentRules(); bool hasIndirectAdjacentRules = current.childrenAffectedByForwardPositionalRules(); #if PLATFORM(IOS) CheckForVisibilityChangeOnRecalcStyle checkForVisibilityChange(current, current->renderStyle()); #endif if (change > NoChange || current.needsStyleRecalc()) current.resetComputedStyle(); if (hasParentStyle && (change >= Inherit || current.needsStyleRecalc())) change = resolveLocal(current, change); if (change != Detach) { StyleResolverParentPusher parentPusher(¤t); RenderStyle* currentStyle = current.renderStyle(); if (ShadowRoot* shadowRoot = current.shadowRoot()) { if (change >= Inherit || shadowRoot->childNeedsStyleRecalc() || shadowRoot->needsStyleRecalc()) { parentPusher.push(); resolveShadowTree(shadowRoot, currentStyle, change); } } current.updateBeforePseudoElement(change); // FIXME: This check is good enough for :hover + foo, but it is not good enough for :hover + foo + bar. // For now we will just worry about the common case, since it's a lot trickier to get the second case right // without doing way too much re-resolution. bool forceCheckOfNextElementSibling = false; bool forceCheckOfAnyElementSibling = false; for (Node* child = current.firstChild(); child; child = child->nextSibling()) { if (child->isTextNode()) { updateTextStyle(*toText(child), currentStyle, change); continue; } if (!child->isElementNode()) continue; Element* childElement = toElement(child); bool childRulesChanged = childElement->needsStyleRecalc() && childElement->styleChangeType() == FullStyleChange; if ((forceCheckOfNextElementSibling || forceCheckOfAnyElementSibling)) childElement->setNeedsStyleRecalc(); if (change >= Inherit || childElement->childNeedsStyleRecalc() || childElement->needsStyleRecalc()) { parentPusher.push(); resolveTree(*childElement, change); } forceCheckOfNextElementSibling = childRulesChanged && hasDirectAdjacentRules; forceCheckOfAnyElementSibling = forceCheckOfAnyElementSibling || (childRulesChanged && hasIndirectAdjacentRules); } current.updateAfterPseudoElement(change); } current.clearNeedsStyleRecalc(); current.clearChildNeedsStyleRecalc(); if (current.hasCustomStyleResolveCallbacks()) current.didRecalcStyle(change); }