void SimplifyMarkupCommand::doApply() { ContainerNode* rootNode = m_firstNode->parentNode(); WillBeHeapVector<RefPtrWillBeMember<ContainerNode>> nodesToRemove; // Walk through the inserted nodes, to see if there are elements that could be removed // without affecting the style. The goal is to produce leaner markup even when starting // from a verbose fragment. // We look at inline elements as well as non top level divs that don't have attributes. for (Node* node = m_firstNode.get(); node && node != m_nodeAfterLast; node = NodeTraversal::next(*node)) { if (node->hasChildren() || (node->isTextNode() && node->nextSibling())) continue; ContainerNode* startingNode = node->parentNode(); if (!startingNode) continue; RenderStyle* startingStyle = startingNode->renderStyle(); if (!startingStyle) continue; ContainerNode* currentNode = startingNode; ContainerNode* topNodeWithStartingStyle = nullptr; while (currentNode != rootNode) { if (currentNode->parentNode() != rootNode && isRemovableBlock(currentNode)) nodesToRemove.append(currentNode); currentNode = currentNode->parentNode(); if (!currentNode) break; if (!currentNode->renderer() || !currentNode->renderer()->isRenderInline() || toRenderInline(currentNode->renderer())->alwaysCreateLineBoxes()) continue; if (currentNode->firstChild() != currentNode->lastChild()) { topNodeWithStartingStyle = 0; break; } if (!currentNode->renderStyle()->visualInvalidationDiff(*startingStyle).hasDifference()) topNodeWithStartingStyle = currentNode; } if (topNodeWithStartingStyle) { for (ContainerNode* node = startingNode; node != topNodeWithStartingStyle; node = node->parentNode()) nodesToRemove.append(node); } } // we perform all the DOM mutations at once. for (size_t i = 0; i < nodesToRemove.size(); ++i) { // FIXME: We can do better by directly moving children from nodesToRemove[i]. int numPrunedAncestors = pruneSubsequentAncestorsToRemove(nodesToRemove, i); if (numPrunedAncestors < 0) continue; removeNodePreservingChildren(nodesToRemove[i], AssumeContentIsAlwaysEditable); i += numPrunedAncestors; } }
bool SharedStyleFinder::canShareStyleWithElement(Element& candidate) const { ASSERT(candidate.supportsStyleSharing()); if (element() == candidate) return false; if (candidate.tagQName() != element().tagQName()) return false; if (candidate.needsStyleRecalc()) return false; RenderStyle* style = candidate.renderStyle(); if (!style) return false; if (!style->isSharable()) return false; ContainerNode* parent = NodeRenderingTraversal::parent(&candidate); if (!parent) return false; RenderStyle* parentStyle = parent->renderStyle(); if (!parentStyle) return false; // The StyleAdjuster will change the display of the renderer depending // on it's parent's display. if (parentStyle->requiresOnlyBlockChildren() != m_renderingParent->renderStyle()->requiresOnlyBlockChildren()) return false; if (m_renderingParent->renderStyle()->inheritedNotEqual(parentStyle)) return false; if (!sharingCandidateHasIdenticalStyleAffectingAttributes(candidate)) return false; if (!sharingCandidateCanShareHostStyles(candidate)) return false; if (!candidate.treeScope().hasSameStyles(element().treeScope())) return false; return true; }
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); }