summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/rendering/RenderCounter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/RenderCounter.cpp')
-rw-r--r--Source/WebCore/rendering/RenderCounter.cpp243
1 files changed, 218 insertions, 25 deletions
diff --git a/Source/WebCore/rendering/RenderCounter.cpp b/Source/WebCore/rendering/RenderCounter.cpp
index 57c54f8..fbd5545 100644
--- a/Source/WebCore/rendering/RenderCounter.cpp
+++ b/Source/WebCore/rendering/RenderCounter.cpp
@@ -24,6 +24,7 @@
#include "CounterNode.h"
#include "Document.h"
+#include "Element.h"
#include "HTMLNames.h"
#include "HTMLOListElement.h"
#include "RenderListItem.h"
@@ -46,11 +47,157 @@ static CounterMaps& counterMaps()
return staticCounterMaps;
}
-static inline RenderObject* previousSiblingOrParent(RenderObject* object)
+// This function processes the renderer tree in the order of the DOM tree
+// including pseudo elements as defined in CSS 2.1.
+// Anonymous renderers are skipped except for those representing pseudo elements.
+static RenderObject* previousInPreOrder(const RenderObject* object)
{
- if (RenderObject* sibling = object->previousSibling())
- return sibling;
- return object->parent();
+ Element* parent;
+ Element* sibling;
+ switch (object->style()->styleType()) {
+ case NOPSEUDO:
+ ASSERT(!object->isAnonymous());
+ parent = toElement(object->node());
+ sibling = parent->previousElementSibling();
+ parent = parent->parentElement();
+ break;
+ case BEFORE:
+ return object->generatingNode()->renderer(); // It is always the generating node's renderer
+ case AFTER:
+ parent = toElement(object->generatingNode());
+ sibling = parent->lastElementChild();
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
+ while (sibling) {
+ if (RenderObject* renderer = sibling->renderer()) {
+ if (RenderObject* after = renderer->afterPseudoElementRenderer())
+ return after;
+ parent = sibling;
+ sibling = sibling->lastElementChild();
+ if (!sibling) {
+ if (RenderObject* before = renderer->beforePseudoElementRenderer())
+ return before;
+ return renderer;
+ }
+ } else
+ sibling = sibling->previousElementSibling();
+ }
+ if (!parent)
+ return 0;
+ RenderObject* renderer = parent->renderer(); // Should never be null
+ if (RenderObject* before = renderer->beforePseudoElementRenderer())
+ return before;
+ return renderer;
+}
+
+// This function processes the renderer tree in the order of the DOM tree
+// including pseudo elements as defined in CSS 2.1.
+// Anonymous renderers are skipped except for those representing pseudo elements.
+static RenderObject* previousSiblingOrParent(const RenderObject* object)
+{
+ Element* parent;
+ Element* sibling;
+ switch (object->style()->styleType()) {
+ case NOPSEUDO:
+ ASSERT(!object->isAnonymous());
+ parent = toElement(object->node());
+ sibling = parent->previousElementSibling();
+ parent = parent->parentElement();
+ break;
+ case BEFORE:
+ return object->generatingNode()->renderer(); // It is always the generating node's renderer
+ case AFTER:
+ parent = toElement(object->generatingNode());
+ sibling = parent->lastElementChild();
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
+ while (sibling) {
+ if (RenderObject* renderer = sibling->renderer()) // This skips invisible nodes
+ return renderer;
+ sibling = sibling->previousElementSibling();
+ }
+ if (parent) {
+ RenderObject* renderer = parent->renderer();
+ if (RenderObject* before = renderer->virtualChildren()->beforePseudoElementRenderer(renderer))
+ return before;
+ return renderer;
+ }
+ return 0;
+}
+
+static Element* parentElement(RenderObject* object)
+{
+ switch (object->style()->styleType()) {
+ case NOPSEUDO:
+ ASSERT(!object->isAnonymous());
+ return toElement(object->node())->parentElement();
+ case BEFORE:
+ case AFTER:
+ return toElement(object->generatingNode());
+ default:
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
+}
+
+static inline bool areRenderersElementsSiblings(RenderObject* first, RenderObject* second)
+{
+ return parentElement(first) == parentElement(second);
+}
+
+// This function processes the renderer tree in the order of the DOM tree
+// including pseudo elements as defined in CSS 2.1.
+// Anonymous renderers are skipped except for those representing pseudo elements.
+static RenderObject* nextInPreOrder(const RenderObject* object, const Element* stayWithin, bool skipDescendants = false)
+{
+ Element* self;
+ Element* child;
+ RenderObject* result;
+ self = toElement(object->generatingNode());
+ if (skipDescendants)
+ goto nextsibling;
+ switch (object->style()->styleType()) {
+ case NOPSEUDO:
+ ASSERT(!object->isAnonymous());
+ result = object->beforePseudoElementRenderer();
+ if (result)
+ return result;
+ break;
+ case BEFORE:
+ break;
+ case AFTER:
+ goto nextsibling;
+ default:
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
+ child = self->firstElementChild();
+ while (true) {
+ while (child) {
+ result = child->renderer();
+ if (result)
+ return result;
+ child = child->nextElementSibling();
+ }
+ result = self->renderer()->afterPseudoElementRenderer();
+ if (result)
+ return result;
+nextsibling:
+ if (self == stayWithin)
+ return 0;
+ child = self->nextElementSibling();
+ self = self->parentElement();
+ if (!self) {
+ ASSERT(!child); // We can only reach this if we are searching beyond the root element
+ return 0; // which cannot have siblings
+ }
+ }
}
static bool planCounter(RenderObject* object, const AtomicString& identifier, bool& isReset, int& value)
@@ -61,10 +208,27 @@ static bool planCounter(RenderObject* object, const AtomicString& identifier, bo
// We can't even look at their styles or we'll see extra resets and increments!
if (object->isText() && !object->isBR())
return false;
-
+ Node* generatingNode = object->generatingNode();
+ // We must have a generating node or else we cannot have a counter.
+ if (!generatingNode)
+ return false;
RenderStyle* style = object->style();
ASSERT(style);
+ switch (style->styleType()) {
+ case NOPSEUDO:
+ // Sometimes nodes have more then one renderer. Only the first one gets the counter
+ // LayoutTests/http/tests/css/counter-crash.html
+ if (generatingNode->renderer() != object)
+ return false;
+ break;
+ case BEFORE:
+ case AFTER:
+ break;
+ default:
+ return false; // Counters are forbidden from all other pseudo elements.
+ }
+
if (const CounterDirectiveMap* directivesMap = style->counterDirectives()) {
CounterDirectives directives = directivesMap->get(identifier.impl());
if (directives.m_reset) {
@@ -133,14 +297,9 @@ static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString&
// We check renderers in preOrder from the renderer that our counter is attached to
// towards the begining of the document for counters with the same identifier as the one
// we are trying to find a place for. This is the next renderer to be checked.
- RenderObject* currentRenderer = counterOwner->previousInPreOrder();
+ RenderObject* currentRenderer = previousInPreOrder(counterOwner);
previousSibling = 0;
while (currentRenderer) {
- // A sibling without a parent means that the counter node tree was not constructed correctly so we stop
- // traversing. In the future RenderCounter should handle RenderObjects that are not connected to the
- // render tree at counter node creation. See bug 43812.
- if (previousSibling && !previousSibling->parent())
- return false;
CounterNode* currentCounter = makeCounterNode(currentRenderer, identifier, false);
if (searchEndRenderer == currentRenderer) {
// We may be at the end of our search.
@@ -149,7 +308,7 @@ static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString&
if (previousSibling) { // But we already found another counter that we come after.
if (currentCounter->actsAsReset()) {
// We found a reset counter that is on a renderer that is a sibling of ours or a parent.
- if (isReset && currentRenderer->parent() == counterOwner->parent()) {
+ if (isReset && areRenderersElementsSiblings(currentRenderer, counterOwner)) {
// We are also a reset counter and the previous reset was on a sibling renderer
// hence we are the next sibling of that counter if that reset is not a root or
// we are a root node if that reset is a root.
@@ -164,7 +323,7 @@ static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString&
return true;
}
// CurrentCounter, the counter at the EndSearchRenderer, is not reset.
- if (!isReset || currentRenderer->parent() != counterOwner->parent()) {
+ if (!isReset || !areRenderersElementsSiblings(currentRenderer, counterOwner)) {
// If the node we are placing is not reset or we have found a counter that is attached
// to an ancestor of the placed counter's renderer we know we are a sibling of that node.
ASSERT(currentCounter->parent() == previousSibling->parent());
@@ -177,7 +336,7 @@ static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString&
// previousSibling, and when we are a sibling of the end counter we must set previousSibling
// to currentCounter.
if (currentCounter->actsAsReset()) {
- if (isReset && currentRenderer->parent() == counterOwner->parent()) {
+ if (isReset && areRenderersElementsSiblings(currentRenderer, counterOwner)) {
parent = currentCounter->parent();
previousSibling = currentCounter;
return parent;
@@ -185,7 +344,7 @@ static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString&
parent = currentCounter;
return true;
}
- if (!isReset || currentRenderer->parent() != counterOwner->parent()) {
+ if (!isReset || !areRenderersElementsSiblings(currentRenderer, counterOwner)) {
parent = currentCounter->parent();
previousSibling = currentCounter;
return true;
@@ -210,7 +369,7 @@ static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString&
previousSibling = currentCounter;
// We are no longer interested in previous siblings of the currentRenderer or their children
// as counters they may have attached cannot be the previous sibling of the counter we are placing.
- currentRenderer = currentRenderer->parent();
+ currentRenderer = parentElement(currentRenderer)->renderer();
continue;
}
} else
@@ -261,28 +420,28 @@ static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& id
object->m_hasCounterNodeMap = true;
}
nodeMap->set(identifier.impl(), newNode);
- if (newNode->parent() || !object->nextInPreOrder(object->parent()))
+ if (newNode->parent())
return newNode.get();
// Checking if some nodes that were previously counter tree root nodes
// should become children of this node now.
CounterMaps& maps = counterMaps();
- RenderObject* stayWithin = object->parent();
- for (RenderObject* currentRenderer = object->nextInPreOrder(stayWithin); currentRenderer; currentRenderer = currentRenderer->nextInPreOrder(stayWithin)) {
+ Element* stayWithin = parentElement(object);
+ bool skipDescendants;
+ for (RenderObject* currentRenderer = nextInPreOrder(object, stayWithin); currentRenderer; currentRenderer = nextInPreOrder(currentRenderer, stayWithin, skipDescendants)) {
+ skipDescendants = false;
if (!currentRenderer->m_hasCounterNodeMap)
continue;
CounterNode* currentCounter = maps.get(currentRenderer)->get(identifier.impl()).get();
if (!currentCounter)
continue;
+ skipDescendants = true;
if (currentCounter->parent()) {
ASSERT(newNode->firstChild());
- if (currentRenderer->lastChild())
- currentRenderer = currentRenderer->lastChild();
continue;
}
- if (stayWithin != currentRenderer->parent() || !currentCounter->hasResetType())
- newNode->insertAfter(currentCounter, newNode->lastChild(), identifier);
- if (currentRenderer->lastChild())
- currentRenderer = currentRenderer->lastChild();
+ if (stayWithin == parentElement(currentRenderer) && currentCounter->hasResetType())
+ break;
+ newNode->insertAfter(currentCounter, newNode->lastChild(), identifier);
}
return newNode.get();
}
@@ -449,12 +608,22 @@ static void updateCounters(RenderObject* renderer)
void RenderCounter::rendererSubtreeAttached(RenderObject* renderer)
{
+ Node* node = renderer->node();
+ if (node)
+ node = node->parentNode();
+ else
+ node = renderer->generatingNode();
+ if (node && !node->attached())
+ return; // No need to update if the parent is not attached yet
for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer))
updateCounters(descendant);
}
void RenderCounter::rendererStyleChanged(RenderObject* renderer, const RenderStyle* oldStyle, const RenderStyle* newStyle)
{
+ Node* node = renderer->generatingNode();
+ if (!node || !node->attached())
+ return; // cannot have generated content or if it can have, it will be handled during attaching
const CounterDirectiveMap* newCounterDirectives;
const CounterDirectiveMap* oldCounterDirectives;
if (oldStyle && (oldCounterDirectives = oldStyle->counterDirectives())) {
@@ -494,3 +663,27 @@ void RenderCounter::rendererStyleChanged(RenderObject* renderer, const RenderSty
}
} // namespace WebCore
+
+#ifndef NDEBUG
+
+void showCounterRendererTree(const WebCore::RenderObject* renderer, const char* counterName)
+{
+ if (!renderer)
+ return;
+ const WebCore::RenderObject* root = renderer;
+ while (root->parent())
+ root = root->parent();
+
+ AtomicString identifier(counterName);
+ for (const WebCore::RenderObject* current = root; current; current = current->nextInPreOrder()) {
+ fprintf(stderr, "%c", (current == renderer) ? '*' : ' ');
+ for (const WebCore::RenderObject* parent = current; parent && parent != root; parent = parent->parent())
+ fprintf(stderr, " ");
+ fprintf(stderr, "%p N:%p P:%p PS:%p NS:%p C:%p\n",
+ current, current->node(), current->parent(), current->previousSibling(),
+ current->nextSibling(), current->m_hasCounterNodeMap?
+ counterName ? WebCore::counterMaps().get(current)->get(identifier.impl()).get() : (WebCore::CounterNode*)1 : (WebCore::CounterNode*)0);
+ }
+}
+
+#endif // NDEBUG