Modified: trunk/Source/WebCore/style/StyleResolveTree.cpp (166302 => 166303)
--- trunk/Source/WebCore/style/StyleResolveTree.cpp 2014-03-26 18:31:58 UTC (rev 166302)
+++ trunk/Source/WebCore/style/StyleResolveTree.cpp 2014-03-26 18:35:55 UTC (rev 166303)
@@ -4,7 +4,7 @@
* (C) 2001 Peter Kelly (p...@post.com)
* (C) 2001 Dirk Mueller (muel...@kde.org)
* (C) 2007 David Smith (catfish....@gmail.com)
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2010, 2012-2014 Apple Inc. All rights reserved.
* (C) 2007 Eric Seidel (e...@webkit.org)
*
* This library is free software; you can redistribute it and/or
@@ -61,11 +61,34 @@
enum DetachType { NormalDetach, ReattachDetach };
-static void attachRenderTree(Element&, ContainerNode& renderingParentNode, PassRefPtr<RenderStyle>);
-static void attachTextRenderer(Text&, ContainerNode& renderingParentNode);
+class RenderTreePosition {
+public:
+ RenderTreePosition(RenderElement* parent);
+ RenderTreePosition(RenderElement* parent, RenderObject* nextSibling);
+
+ RenderElement* parent() { return m_parent; }
+
+ void insert(RenderObject&);
+ bool canInsert(RenderElement&) const;
+ bool canInsert(RenderText&) const;
+
+ void computeNextSibling(const Node&, ContainerNode& renderingParentNode);
+ void invalidateNextSibling(const RenderObject&);
+
+private:
+ RenderElement* m_parent;
+ RenderObject* m_nextSibling;
+ bool m_hasValidNextSibling;
+#if !ASSERT_DISABLED
+ unsigned m_assertionLimitCounter;
+#endif
+};
+
+static void attachRenderTree(Element&, ContainerNode& renderingParentNode, RenderTreePosition&, PassRefPtr<RenderStyle>);
+static void attachTextRenderer(Text&, ContainerNode& renderingParentNode, RenderTreePosition&);
static void detachRenderTree(Element&, DetachType);
-static void resolveTextNode(Text&, ContainerNode& renderingParentNode);
-static void resolveTree(Element&, ContainerNode& renderingParentNode, Change);
+static void resolveTextNode(Text&, ContainerNode& renderingParentNode, RenderTreePosition&);
+static void resolveTree(Element&, ContainerNode& renderingParentNode, RenderTreePosition&, Change);
Change determineChange(const RenderStyle* s1, const RenderStyle* s2)
{
@@ -151,6 +174,67 @@
return nullptr;
}
+RenderTreePosition::RenderTreePosition(RenderElement* parent)
+ : m_parent(parent)
+ , m_nextSibling(nullptr)
+ , m_hasValidNextSibling(false)
+#if !ASSERT_DISABLED
+ , m_assertionLimitCounter(0)
+#endif
+{
+}
+
+RenderTreePosition::RenderTreePosition(RenderElement* parent, RenderObject* nextSibling)
+ : m_parent(parent)
+ , m_nextSibling(nextSibling)
+ , m_hasValidNextSibling(true)
+#if !ASSERT_DISABLED
+ , m_assertionLimitCounter(0)
+#endif
+{
+}
+
+bool RenderTreePosition::canInsert(RenderElement& renderer) const
+{
+ ASSERT(m_parent);
+ ASSERT(!renderer.parent());
+ return m_parent->isChildAllowed(renderer, renderer.style());
+}
+
+bool RenderTreePosition::canInsert(RenderText& renderer) const
+{
+ ASSERT(m_parent);
+ ASSERT(!renderer.parent());
+ return m_parent->isChildAllowed(renderer, m_parent->style());
+}
+
+void RenderTreePosition::insert(RenderObject& renderer)
+{
+ ASSERT(m_parent);
+ ASSERT(m_hasValidNextSibling);
+ m_parent->addChild(&renderer, m_nextSibling);
+}
+
+void RenderTreePosition::computeNextSibling(const Node& node, ContainerNode& renderingParentNode)
+{
+ ASSERT(!node.renderer());
+ if (m_hasValidNextSibling) {
+ // Stop validating at some point so the assert doesn't make us O(N^2) on debug builds.
+ ASSERT(++m_assertionLimitCounter > 20 || nextSiblingRenderer(node, renderingParentNode) == m_nextSibling);
+ return;
+ }
+ m_nextSibling = nextSiblingRenderer(node, renderingParentNode);
+ m_hasValidNextSibling = true;
+}
+
+void RenderTreePosition::invalidateNextSibling(const RenderObject& siblingRenderer)
+{
+ if (!m_hasValidNextSibling)
+ return;
+ if (m_nextSibling == &siblingRenderer)
+ m_hasValidNextSibling = false;
+}
+
static bool shouldCreateRenderer(const Element& element, const ContainerNode& renderingParent)
{
if (!element.document().shouldCreateRenderers())
@@ -165,17 +249,18 @@
return true;
}
-static PassRef<RenderStyle> styleForElement(Element& element, RenderStyle& parentStyle)
+static PassRef<RenderStyle> styleForElement(Element& element, ContainerNode& renderingParentNode)
{
- if (element.hasCustomStyleResolveCallbacks()) {
- if (RefPtr<RenderStyle> style = element.customStyleForRenderer(parentStyle))
+ RenderStyle* parentStyle = renderingParentNode.renderStyle();
+ if (element.hasCustomStyleResolveCallbacks() && parentStyle) {
+ if (RefPtr<RenderStyle> style = element.customStyleForRenderer(*parentStyle))
return style.releaseNonNull();
}
- return element.document().ensureStyleResolver().styleForElement(&element, &parentStyle);
+ return element.document().ensureStyleResolver().styleForElement(&element, parentStyle);
}
// Check the specific case of elements that are children of regions but are flowed into a flow thread themselves.
-static bool elementInsideRegionNeedsRenderer(Element& element, const ContainerNode& renderingParentNode, RefPtr<RenderStyle>& style)
+static bool elementInsideRegionNeedsRenderer(Element& element, ContainerNode& renderingParentNode, RefPtr<RenderStyle>& style)
{
#if ENABLE(CSS_REGIONS)
// The parent of a region should always be an element.
@@ -187,7 +272,7 @@
return false;
if (!style)
- style = styleForElement(element, *renderingParentNode.renderStyle());
+ style = styleForElement(element, renderingParentNode);
// Children of this element will only be allowed to be flowed into other flow-threads if display is NOT none.
if (element.rendererIsNeeded(*style))
@@ -215,7 +300,7 @@
}
#endif
-static void createRendererIfNeeded(Element& element, ContainerNode& renderingParentNode, PassRefPtr<RenderStyle> resolvedStyle)
+static void createRendererIfNeeded(Element& element, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition, PassRefPtr<RenderStyle> resolvedStyle)
{
ASSERT(!element.renderer());
@@ -227,7 +312,7 @@
return;
if (!style)
- style = styleForElement(element, *renderingParentNode.renderStyle());
+ style = styleForElement(element, renderingParentNode);
RenderNamedFlowThread* parentFlowRenderer = 0;
#if ENABLE(CSS_REGIONS)
@@ -237,28 +322,23 @@
if (!element.rendererIsNeeded(*style))
return;
- RenderElement* parentRenderer;
- RenderObject* nextRenderer;
- if (parentFlowRenderer) {
- parentRenderer = parentFlowRenderer;
- nextRenderer = parentFlowRenderer->nextRendererForElement(element);
- } else {
- // FIXME: Make this path Element only, handle the root special case separately.
- parentRenderer = renderingParentNode.renderer();
- nextRenderer = nextSiblingRenderer(element, renderingParentNode);
- }
+ renderTreePosition.computeNextSibling(element, renderingParentNode);
+ RenderTreePosition insertionPosition = parentFlowRenderer
+ ? RenderTreePosition(parentFlowRenderer, parentFlowRenderer->nextRendererForElement(element))
+ : renderTreePosition;
+
RenderElement* newRenderer = element.createElementRenderer(style.releaseNonNull()).leakPtr();
if (!newRenderer)
return;
- if (!parentRenderer->isChildAllowed(*newRenderer, newRenderer->style())) {
+ if (!insertionPosition.canInsert(*newRenderer)) {
newRenderer->destroy();
return;
}
// Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style
// for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail.
- newRenderer->setFlowThreadState(parentRenderer->flowThreadState());
+ newRenderer->setFlowThreadState(insertionPosition.parent()->flowThreadState());
// Code below updateAnimations() can depend on Element::renderer() already being set.
element.setRenderer(newRenderer);
@@ -272,13 +352,13 @@
#if ENABLE(FULLSCREEN_API)
Document& document = element.document();
if (document.webkitIsFullScreen() && document.webkitCurrentFullScreenElement() == &element) {
- newRenderer = RenderFullScreen::wrapRenderer(newRenderer, parentRenderer, document);
+ newRenderer = RenderFullScreen::wrapRenderer(newRenderer, insertionPosition.parent(), document);
if (!newRenderer)
return;
}
#endif
// Note: Adding newRenderer instead of renderer(). renderer() may be a child of newRenderer.
- parentRenderer->addChild(newRenderer, nextRenderer);
+ insertionPosition.insert(*newRenderer);
}
static RenderObject* previousSiblingRenderer(const Text& textNode)
@@ -367,34 +447,33 @@
return true;
}
-static void createTextRendererIfNeeded(Text& textNode, ContainerNode& renderingParentNode)
+static void createTextRendererIfNeeded(Text& textNode, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition)
{
ASSERT(!textNode.renderer());
if (!textRendererIsNeeded(textNode, renderingParentNode))
return;
- RenderElement& parentRenderer = *renderingParentNode.renderer();
- const auto& style = parentRenderer.style();
- auto newRenderer = textNode.createTextRenderer(style);
+ auto newRenderer = textNode.createTextRenderer(*renderingParentNode.renderStyle());
ASSERT(newRenderer);
- if (!parentRenderer.isChildAllowed(*newRenderer, style))
+ renderTreePosition.computeNextSibling(textNode, renderingParentNode);
+
+ if (!renderTreePosition.canInsert(*newRenderer))
return;
// Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style
// for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail.
- newRenderer->setFlowThreadState(parentRenderer.flowThreadState());
+ newRenderer->setFlowThreadState(renderTreePosition.parent()->flowThreadState());
- RenderObject* nextRenderer = nextSiblingRenderer(textNode, renderingParentNode);
textNode.setRenderer(newRenderer.get());
// Parent takes care of the animations, no need to call setAnimatableStyle.
- parentRenderer.addChild(newRenderer.leakPtr(), nextRenderer);
+ renderTreePosition.insert(*newRenderer.leakPtr());
}
-void attachTextRenderer(Text& textNode, ContainerNode& renderingParent)
+void attachTextRenderer(Text& textNode, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition)
{
- createTextRendererIfNeeded(textNode, renderingParent);
+ createTextRendererIfNeeded(textNode, renderingParentNode, renderTreePosition);
textNode.clearNeedsStyleRecalc();
}
@@ -413,55 +492,64 @@
return;
bool hadRenderer = textNode.renderer();
- resolveTextNode(textNode, *renderingParentNode);
+ RenderTreePosition renderTreePosition(renderingParentNode->renderer());
+ resolveTextNode(textNode, *renderingParentNode, renderTreePosition);
+
if (hadRenderer && textNode.renderer())
textNode.renderer()->setTextWithOffset(textNode.dataImpl(), offsetOfReplacedData, lengthOfReplacedData);
}
-static void attachChildren(ContainerNode& current, ContainerNode& renderingParentNode)
+static void attachChildren(ContainerNode& current, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition)
{
for (Node* child = current.firstChild(); child; child = child->nextSibling()) {
ASSERT((!child->renderer() || child->inNamedFlow()) || current.shadowRoot());
- if (child->renderer())
+ if (child->renderer()) {
+ renderTreePosition.invalidateNextSibling(*child->renderer());
continue;
+ }
if (child->isTextNode()) {
- attachTextRenderer(*toText(child), renderingParentNode);
+ attachTextRenderer(*toText(child), renderingParentNode, renderTreePosition);
continue;
}
if (child->isElementNode())
- attachRenderTree(*toElement(child), renderingParentNode, nullptr);
+ attachRenderTree(*toElement(child), renderingParentNode, renderTreePosition, nullptr);
}
}
-static void attachDistributedChildren(InsertionPoint& insertionPoint, ContainerNode& renderingParentNode)
+static void attachDistributedChildren(InsertionPoint& insertionPoint, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition)
{
if (ShadowRoot* shadowRoot = insertionPoint.containingShadowRoot())
ContentDistributor::ensureDistribution(shadowRoot);
for (Node* current = insertionPoint.firstDistributed(); current; current = insertionPoint.nextDistributedTo(current)) {
+ if (current->renderer())
+ renderTreePosition.invalidateNextSibling(*current->renderer());
if (current->isTextNode()) {
if (current->renderer())
continue;
- attachTextRenderer(*toText(current), renderingParentNode);
+ attachTextRenderer(*toText(current), renderingParentNode, renderTreePosition);
continue;
}
if (current->isElementNode()) {
Element& currentElement = toElement(*current);
if (currentElement.renderer())
detachRenderTree(currentElement);
- attachRenderTree(currentElement, renderingParentNode, nullptr);
+ attachRenderTree(currentElement, renderingParentNode, renderTreePosition, nullptr);
}
}
// Use actual children as fallback content.
if (!insertionPoint.hasDistribution())
- attachChildren(insertionPoint, renderingParentNode);
+ attachChildren(insertionPoint, renderingParentNode, renderTreePosition);
}
static void attachShadowRoot(ShadowRoot& shadowRoot)
{
- attachChildren(shadowRoot, *shadowRoot.hostElement());
+ ASSERT(shadowRoot.hostElement());
+ RenderTreePosition renderTreePosition(shadowRoot.hostElement()->renderer());
+ attachChildren(shadowRoot, *shadowRoot.hostElement(), renderTreePosition);
+
shadowRoot.clearNeedsStyleRecalc();
shadowRoot.clearChildNeedsStyleRecalc();
}
@@ -507,16 +595,16 @@
return true;
}
-static void attachBeforeOrAfterPseudoElementIfNeeded(Element& current, PseudoId pseudoId)
+static void attachBeforeOrAfterPseudoElementIfNeeded(Element& current, PseudoId pseudoId, RenderTreePosition& renderTreePosition)
{
if (!needsPseudoElement(current, pseudoId))
return;
RefPtr<PseudoElement> pseudoElement = PseudoElement::create(current, pseudoId);
setBeforeOrAfterPseudoElement(current, pseudoElement, pseudoId);
- attachRenderTree(*pseudoElement, current, nullptr);
+ attachRenderTree(*pseudoElement, current, renderTreePosition, nullptr);
}
-static void attachRenderTree(Element& current, ContainerNode& renderingParentNode, PassRefPtr<RenderStyle> resolvedStyle)
+static void attachRenderTree(Element& current, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition, PassRefPtr<RenderStyle> resolvedStyle)
{
PostResolutionCallbackDisabler callbackDisabler(current.document());
WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
@@ -524,15 +612,16 @@
if (current.hasCustomStyleResolveCallbacks())
current.willAttachRenderers();
- createRendererIfNeeded(current, renderingParentNode, resolvedStyle);
+ createRendererIfNeeded(current, renderingParentNode, renderTreePosition, resolvedStyle);
if (current.parentElement() && current.parentElement()->isInCanvasSubtree())
current.setIsInCanvasSubtree(true);
- attachBeforeOrAfterPseudoElementIfNeeded(current, BEFORE);
-
StyleResolverParentPusher parentPusher(¤t);
+ RenderTreePosition childRenderTreePosition(current.renderer());
+ attachBeforeOrAfterPseudoElementIfNeeded(current, BEFORE, childRenderTreePosition);
+
if (ShadowRoot* shadowRoot = current.shadowRoot()) {
parentPusher.push();
attachShadowRoot(*shadowRoot);
@@ -540,9 +629,9 @@
parentPusher.push();
if (isInsertionPoint(current))
- attachDistributedChildren(toInsertionPoint(current), renderingParentNode);
+ attachDistributedChildren(toInsertionPoint(current), renderingParentNode, renderTreePosition);
else
- attachChildren(current, current);
+ attachChildren(current, current, childRenderTreePosition);
current.clearNeedsStyleRecalc();
current.clearChildNeedsStyleRecalc();
@@ -550,7 +639,7 @@
if (AXObjectCache* cache = current.document().axObjectCache())
cache->updateCacheAfterNodeIsAttached(¤t);
- attachBeforeOrAfterPseudoElementIfNeeded(current, AFTER);
+ attachBeforeOrAfterPseudoElementIfNeeded(current, AFTER, childRenderTreePosition);
current.updateFocusAppearanceAfterAttachIfNeeded();
@@ -652,7 +741,7 @@
return false;
}
-static Change resolveLocal(Element& current, ContainerNode& renderingParentNode, Change inheritedChange)
+static Change resolveLocal(Element& current, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition, Change inheritedChange)
{
Change localChange = Detach;
RefPtr<RenderStyle> newStyle;
@@ -660,13 +749,13 @@
Document& document = current.document();
if (currentStyle && current.styleChangeType() != ReconstructRenderTree) {
- newStyle = styleForElement(current, *renderingParentNode.renderStyle());
+ newStyle = styleForElement(current, renderingParentNode);
localChange = determineChange(currentStyle.get(), newStyle.get());
}
if (localChange == Detach) {
if (current.renderer() || current.inNamedFlow())
detachRenderTree(current, ReattachDetach);
- attachRenderTree(current, renderingParentNode, newStyle.release());
+ attachRenderTree(current, renderingParentNode, renderTreePosition, newStyle.release());
invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(current);
return Detach;
@@ -698,7 +787,7 @@
return localChange;
}
-void resolveTextNode(Text& text, ContainerNode& renderingParentNode)
+void resolveTextNode(Text& text, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition)
{
text.clearNeedsStyleRecalc();
@@ -713,35 +802,39 @@
}
if (!needsRenderer)
return;
- attachTextRenderer(text, renderingParentNode);
+ attachTextRenderer(text, renderingParentNode, renderTreePosition);
invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(text);
}
-static void resolveShadowTree(ShadowRoot& shadowRoot, Style::Change change)
+static void resolveShadowTree(ShadowRoot& shadowRoot, Element& host, Style::Change change)
{
+ ASSERT(shadowRoot.hostElement() == &host);
+ RenderTreePosition renderTreePosition(host.renderer());
for (Node* child = shadowRoot.firstChild(); child; child = child->nextSibling()) {
+ if (child->renderer())
+ renderTreePosition.invalidateNextSibling(*child->renderer());
if (child->isTextNode() && child->needsStyleRecalc()) {
- resolveTextNode(*toText(child), *shadowRoot.hostElement());
+ resolveTextNode(*toText(child), host, renderTreePosition);
continue;
}
if (child->isElementNode())
- resolveTree(*toElement(child), *shadowRoot.hostElement(), change);
+ resolveTree(*toElement(child), host, renderTreePosition, change);
}
shadowRoot.clearNeedsStyleRecalc();
shadowRoot.clearChildNeedsStyleRecalc();
}
-static void updateBeforeOrAfterPseudoElement(Element& current, Change change, PseudoId pseudoId)
+static void updateBeforeOrAfterPseudoElement(Element& current, Change change, PseudoId pseudoId, RenderTreePosition& renderTreePosition)
{
if (PseudoElement* existingPseudoElement = beforeOrAfterPseudoElement(current, pseudoId)) {
if (needsPseudoElement(current, pseudoId))
- resolveTree(*existingPseudoElement, current, current.needsStyleRecalc() ? Force : change);
+ resolveTree(*existingPseudoElement, current, renderTreePosition, current.needsStyleRecalc() ? Force : change);
else
clearBeforeOrAfterPseudoElement(current, pseudoId);
return;
}
- attachBeforeOrAfterPseudoElementIfNeeded(current, pseudoId);
+ attachBeforeOrAfterPseudoElementIfNeeded(current, pseudoId, renderTreePosition);
}
#if PLATFORM(IOS)
@@ -796,7 +889,7 @@
};
#endif // PLATFORM(IOS)
-void resolveTree(Element& current, ContainerNode& renderingParentNode, Change change)
+void resolveTree(Element& current, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition, Change change)
{
ASSERT(change != Detach);
@@ -817,7 +910,7 @@
current.resetComputedStyle();
if (hasParentStyle && (change >= Inherit || current.needsStyleRecalc()))
- change = resolveLocal(current, renderingParentNode, change);
+ change = resolveLocal(current, renderingParentNode, renderTreePosition, change);
if (change != Detach) {
StyleResolverParentPusher parentPusher(¤t);
@@ -825,11 +918,12 @@
if (ShadowRoot* shadowRoot = current.shadowRoot()) {
if (change >= Inherit || shadowRoot->childNeedsStyleRecalc() || shadowRoot->needsStyleRecalc()) {
parentPusher.push();
- resolveShadowTree(*shadowRoot, change);
+ resolveShadowTree(*shadowRoot, current, change);
}
}
- updateBeforeOrAfterPseudoElement(current, change, BEFORE);
+ RenderTreePosition childRenderTreePosition(current.renderer());
+ updateBeforeOrAfterPseudoElement(current, change, BEFORE, childRenderTreePosition);
// 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
@@ -837,8 +931,10 @@
bool forceCheckOfNextElementSibling = false;
bool forceCheckOfAnyElementSibling = false;
for (Node* child = current.firstChild(); child; child = child->nextSibling()) {
+ if (child->renderer())
+ childRenderTreePosition.invalidateNextSibling(*child->renderer());
if (child->isTextNode() && child->needsStyleRecalc()) {
- resolveTextNode(*toText(child), current);
+ resolveTextNode(*toText(child), current, childRenderTreePosition);
continue;
}
if (!child->isElementNode())
@@ -849,13 +945,13 @@
childElement->setNeedsStyleRecalc();
if (change >= Inherit || childElement->childNeedsStyleRecalc() || childElement->needsStyleRecalc()) {
parentPusher.push();
- resolveTree(*childElement, current, change);
+ resolveTree(*childElement, current, childRenderTreePosition, change);
}
forceCheckOfNextElementSibling = childRulesChanged && hasDirectAdjacentRules;
forceCheckOfAnyElementSibling = forceCheckOfAnyElementSibling || (childRulesChanged && hasIndirectAdjacentRules);
}
- updateBeforeOrAfterPseudoElement(current, change, AFTER);
+ updateBeforeOrAfterPseudoElement(current, change, AFTER, childRenderTreePosition);
}
current.clearNeedsStyleRecalc();
@@ -890,7 +986,8 @@
return;
if (change < Inherit && !documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc())
return;
- resolveTree(*documentElement, document, change);
+ RenderTreePosition renderTreePosition(document.renderView());
+ resolveTree(*documentElement, document, renderTreePosition, change);
}
void detachRenderTree(Element& element)