| Index: Source/WebCore/rendering/RenderCounter.cpp
|
| ===================================================================
|
| --- Source/WebCore/rendering/RenderCounter.cpp (revision 79931)
|
| +++ Source/WebCore/rendering/RenderCounter.cpp (working copy)
|
| @@ -24,6 +24,7 @@
|
|
|
| #include "CounterNode.h"
|
| #include "Document.h"
|
| +#include "Element.h"
|
| #include "HTMLNames.h"
|
| #include "HTMLOListElement.h"
|
| #include "RenderListItem.h"
|
| @@ -46,13 +47,159 @@
|
| 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)
|
| {
|
| ASSERT(object);
|
| @@ -61,10 +208,27 @@
|
| // 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,7 +297,7 @@
|
| // 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) {
|
| CounterNode* currentCounter = makeCounterNode(currentRenderer, identifier, false);
|
| @@ -144,7 +308,7 @@
|
| 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.
|
| @@ -159,7 +323,7 @@
|
| 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());
|
| @@ -172,7 +336,7 @@
|
| // 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;
|
| @@ -180,7 +344,7 @@
|
| parent = currentCounter;
|
| return true;
|
| }
|
| - if (!isReset || currentRenderer->parent() != counterOwner->parent()) {
|
| + if (!isReset || !areRenderersElementsSiblings(currentRenderer, counterOwner)) {
|
| parent = currentCounter->parent();
|
| previousSibling = currentCounter;
|
| return true;
|
| @@ -205,7 +369,7 @@
|
| 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
|
| @@ -256,28 +420,28 @@
|
| 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();
|
| }
|
| @@ -444,12 +608,22 @@
|
|
|
| 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())) {
|
| @@ -489,3 +663,27 @@
|
| }
|
|
|
| } // 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
|
|
|