Index: Source/core/dom/Element.cpp |
diff --git a/Source/core/dom/Element.cpp b/Source/core/dom/Element.cpp |
index 56e69b05bac772a198385b665d19f8c410ddd634..707678c968dee84f0010477443b4f981db45dc10 100644 |
--- a/Source/core/dom/Element.cpp |
+++ b/Source/core/dom/Element.cpp |
@@ -1514,6 +1514,15 @@ StyleRecalcChange Element::recalcOwnStyle(StyleRecalcChange change) |
return max(localChange, change); |
} |
+static const int maxWhitespaceChildrenToDefer = 10; |
+typedef Vector<Text*, maxWhitespaceChildrenToDefer> DeferredWhitespaceChildren; |
+ |
+static inline void recalcWhitespaceChildrenStyle(DeferredWhitespaceChildren& whitespaceChildren, StyleRecalcChange change) |
+{ |
+ for (int i = whitespaceChildren.size() - 1; i >= 0; --i) |
+ whitespaceChildren[i]->recalcTextStyle(change); |
+} |
+ |
void Element::recalcChildStyle(StyleRecalcChange change) |
{ |
ASSERT(document().inStyleRecalc()); |
@@ -1537,29 +1546,41 @@ void Element::recalcChildStyle(StyleRecalcChange change) |
bool hasIndirectAdjacentRules = childrenAffectedByForwardPositionalRules(); |
bool forceCheckOfNextElementSibling = false; |
bool forceCheckOfAnyElementSibling = false; |
- bool forceReattachOfAnyWhitespaceSibling = false; |
- for (Node* child = firstChild(); child; child = child->nextSibling()) { |
- bool didReattach = false; |
- |
- if (child->renderer()) |
- forceReattachOfAnyWhitespaceSibling = false; |
- |
- if (child->isTextNode()) { |
- if (forceReattachOfAnyWhitespaceSibling && toText(child)->containsOnlyWhitespace()) |
- child->reattach(); |
- else |
- didReattach = toText(child)->recalcTextStyle(change); |
- } else if (child->isElementNode()) { |
+ if (hasDirectAdjacentRules || hasIndirectAdjacentRules) { |
+ for (Node* child = firstChild(); child; child = child->nextSibling()) { |
+ if (!child->isElementNode()) |
+ continue; |
Element* element = toElement(child); |
- |
bool childRulesChanged = element->needsStyleRecalc() && element->styleChangeType() >= SubtreeStyleChange; |
- |
if (forceCheckOfNextElementSibling || forceCheckOfAnyElementSibling) |
element->setNeedsStyleRecalc(); |
- |
forceCheckOfNextElementSibling = childRulesChanged && hasDirectAdjacentRules; |
forceCheckOfAnyElementSibling = forceCheckOfAnyElementSibling || (childRulesChanged && hasIndirectAdjacentRules); |
+ } |
+ } |
+ // This loop is deliberately backwards because we use insertBefore in the rendering tree, and want to avoid |
+ // a potentially n^2 loop to find the insertion point while resolving style. Having us start from the last |
+ // child and work our way back means in the common case, we'll find the insertion point in O(1) time. |
+ // Reversing this loop can lead to non-deterministic results in our code to optimize out empty whitespace |
+ // RenderTexts. We try to put off recalcing their style until the end to avoid this issue. |
+ // See crbug.com/288225 |
+ DeferredWhitespaceChildren whitespaceChildren; |
+ for (Node* child = lastChild(); child; child = child->previousSibling()) { |
+ bool didReattach = false; |
+ if (child->isTextNode()) { |
+ Text* textChild = toText(child); |
+ if (textChild->containsOnlyWhitespace()) { |
esprehn
2013/09/30 22:55:34
This also makes us do the string scan for whitespa
|
+ if (whitespaceChildren.size() == maxWhitespaceChildrenToDefer) { |
+ recalcWhitespaceChildrenStyle(whitespaceChildren, change); |
+ whitespaceChildren.clear(); |
+ } |
+ whitespaceChildren.append(textChild); |
+ } else { |
+ didReattach = toText(child)->recalcTextStyle(change); |
+ } |
+ } else if (child->isElementNode()) { |
+ Element* element = toElement(child); |
if (shouldRecalcStyle(change, element)) { |
parentPusher.push(); |
didReattach = element->recalcStyle(change); |
@@ -1568,9 +1589,12 @@ void Element::recalcChildStyle(StyleRecalcChange change) |
} |
} |
- forceReattachOfAnyWhitespaceSibling = didReattach || forceReattachOfAnyWhitespaceSibling; |
+ if (didReattach) |
+ child->reattachWhitespaceSiblings(); |
} |
+ recalcWhitespaceChildrenStyle(whitespaceChildren, change); |
+ |
if (shouldRecalcStyle(change, this)) { |
updatePseudoElement(AFTER, change); |
updatePseudoElement(BACKDROP, change); |
@@ -1740,7 +1764,7 @@ static void checkForSiblingStyleChanges(Element* e, RenderStyle* style, bool fin |
// The + selector. We need to invalidate the first element following the insertion point. It is the only possible element |
// that could be affected by this DOM change. |
if (e->childrenAffectedByDirectAdjacentRules() && afterChange) { |
- if (Node* firstElementAfterInsertion = afterChange->nextElementSibling()) |
+ if (Node* firstElementAfterInsertion = afterChange->isElementNode() ? afterChange : afterChange->nextElementSibling()) |
firstElementAfterInsertion->setNeedsStyleRecalc(); |
} |
} |