Move Source/WebCore/rendering/ code to std::unique_ptr
[WebKit-https.git] / Source / WebCore / rendering / RenderCounter.cpp
1 /**
2  * Copyright (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com)
3  * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  */
21
22 #include "config.h"
23 #include "RenderCounter.h"
24
25 #include "CounterNode.h"
26 #include "Document.h"
27 #include "Element.h"
28 #include "ElementTraversal.h"
29 #include "HTMLNames.h"
30 #include "HTMLOListElement.h"
31 #include "PseudoElement.h"
32 #include "RenderListItem.h"
33 #include "RenderListMarker.h"
34 #include "RenderStyle.h"
35 #include "RenderView.h"
36 #include <wtf/StdLibExtras.h>
37
38 #ifndef NDEBUG
39 #include <stdio.h>
40 #endif
41
42 namespace WebCore {
43
44 using namespace HTMLNames;
45
46 typedef HashMap<AtomicString, RefPtr<CounterNode>> CounterMap;
47 typedef HashMap<const RenderObject*, std::unique_ptr<CounterMap>> CounterMaps;
48
49 static CounterNode* makeCounterNode(RenderObject*, const AtomicString& identifier, bool alwaysCreateCounter);
50
51 static CounterMaps& counterMaps()
52 {
53     DEPRECATED_DEFINE_STATIC_LOCAL(CounterMaps, staticCounterMaps, ());
54     return staticCounterMaps;
55 }
56
57 // This function processes the renderer tree in the order of the DOM tree
58 // including pseudo elements as defined in CSS 2.1.
59 static RenderObject* previousInPreOrder(const RenderObject* object)
60 {
61     Element* self = toElement(object->node());
62     Element* previous = ElementTraversal::previousIncludingPseudo(self);
63     while (previous && !previous->renderer())
64         previous = ElementTraversal::previousIncludingPseudo(previous);
65     return previous ? previous->renderer() : 0;
66 }
67
68 static inline Element* parentOrPseudoHostElement(const RenderObject* object)
69 {
70     if (object->node()->isPseudoElement())
71         return toPseudoElement(object->node())->hostElement();
72     return toElement(object->node())->parentElement();
73 }
74
75 // This function processes the renderer tree in the order of the DOM tree
76 // including pseudo elements as defined in CSS 2.1.
77 static RenderObject* previousSiblingOrParent(const RenderObject* object)
78 {
79     Element* self = toElement(object->node());
80     Element* previous = ElementTraversal::pseudoAwarePreviousSibling(self);
81     while (previous && !previous->renderer())
82         previous = ElementTraversal::pseudoAwarePreviousSibling(previous);
83     if (previous)
84         return previous->renderer();
85     previous = parentOrPseudoHostElement(object);
86     return previous ? previous->renderer() : 0;
87 }
88
89 static inline bool areRenderersElementsSiblings(RenderObject* first, RenderObject* second)
90 {
91     return parentOrPseudoHostElement(first) == parentOrPseudoHostElement(second);
92 }
93
94 // This function processes the renderer tree in the order of the DOM tree
95 // including pseudo elements as defined in CSS 2.1.
96 static RenderElement* nextInPreOrder(const RenderElement* element, const Element* stayWithin, bool skipDescendants = false)
97 {
98     Element* self = element->element();
99     Element* next = skipDescendants ? ElementTraversal::nextIncludingPseudoSkippingChildren(self, stayWithin) : ElementTraversal::nextIncludingPseudo(self, stayWithin);
100     while (next && !next->renderer())
101         next = skipDescendants ? ElementTraversal::nextIncludingPseudoSkippingChildren(next, stayWithin) : ElementTraversal::nextIncludingPseudo(next, stayWithin);
102     return next ? next->renderer() : 0;
103 }
104
105 static bool planCounter(RenderElement* object, const AtomicString& identifier, bool& isReset, int& value)
106 {
107     ASSERT(object);
108
109     // We must have a generating node or else we cannot have a counter.
110     Element* generatingElement = object->generatingElement();
111     if (!generatingElement)
112         return false;
113
114     const RenderStyle& style = object->style();
115
116     switch (style.styleType()) {
117     case NOPSEUDO:
118         // Sometimes elements have more then one renderer. Only the first one gets the counter
119         // LayoutTests/http/tests/css/counter-crash.html
120         if (generatingElement->renderer() != object)
121             return false;
122         break;
123     case BEFORE:
124     case AFTER:
125         break;
126     default:
127         return false; // Counters are forbidden from all other pseudo elements.
128     }
129
130     const CounterDirectives directives = style.getCounterDirectives(identifier);
131     if (directives.isDefined()) {
132         value = directives.combinedValue();
133         isReset = directives.isReset();
134         return true;
135     }
136
137     if (identifier == "list-item") {
138         if (object->isListItem()) {
139             if (toRenderListItem(object)->hasExplicitValue()) {
140                 value = toRenderListItem(object)->explicitValue();
141                 isReset = true;
142                 return true;
143             }
144             value = 1;
145             isReset = false;
146             return true;
147         }
148         if (Element* e = object->element()) {
149             if (e->hasTagName(olTag)) {
150                 value = toHTMLOListElement(e)->start();
151                 isReset = true;
152                 return true;
153             }
154             if (e->hasTagName(ulTag) || e->hasTagName(menuTag) || e->hasTagName(dirTag)) {
155                 value = 0;
156                 isReset = true;
157                 return true;
158             }
159         }
160     }
161
162     return false;
163 }
164
165 // - Finds the insertion point for the counter described by counterOwner, isReset and 
166 // identifier in the CounterNode tree for identifier and sets parent and
167 // previousSibling accordingly.
168 // - The function returns true if the counter whose insertion point is searched is NOT
169 // the root of the tree.
170 // - The root of the tree is a counter reference that is not in the scope of any other
171 // counter with the same identifier.
172 // - All the counter references with the same identifier as this one that are in
173 // children or subsequent siblings of the renderer that owns the root of the tree
174 // form the rest of of the nodes of the tree.
175 // - The root of the tree is always a reset type reference.
176 // - A subtree rooted at any reset node in the tree is equivalent to all counter 
177 // references that are in the scope of the counter or nested counter defined by that
178 // reset node.
179 // - Non-reset CounterNodes cannot have descendants.
180
181 static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& identifier, bool isReset, RefPtr<CounterNode>& parent, RefPtr<CounterNode>& previousSibling)
182 {
183     // We cannot stop searching for counters with the same identifier before we also
184     // check this renderer, because it may affect the positioning in the tree of our counter.
185     RenderObject* searchEndRenderer = previousSiblingOrParent(counterOwner);
186     // We check renderers in preOrder from the renderer that our counter is attached to
187     // towards the begining of the document for counters with the same identifier as the one
188     // we are trying to find a place for. This is the next renderer to be checked.
189     RenderObject* currentRenderer = previousInPreOrder(counterOwner);
190     previousSibling = 0;
191     RefPtr<CounterNode> previousSiblingProtector = 0;
192
193     while (currentRenderer) {
194         CounterNode* currentCounter = makeCounterNode(currentRenderer, identifier, false);
195         if (searchEndRenderer == currentRenderer) {
196             // We may be at the end of our search.
197             if (currentCounter) {
198                 // We have a suitable counter on the EndSearchRenderer.
199                 if (previousSiblingProtector) { // But we already found another counter that we come after.
200                     if (currentCounter->actsAsReset()) {
201                         // We found a reset counter that is on a renderer that is a sibling of ours or a parent.
202                         if (isReset && areRenderersElementsSiblings(currentRenderer, counterOwner)) {
203                             // We are also a reset counter and the previous reset was on a sibling renderer
204                             // hence we are the next sibling of that counter if that reset is not a root or
205                             // we are a root node if that reset is a root.
206                             parent = currentCounter->parent();
207                             previousSibling = parent ? currentCounter : 0;
208                             return parent;
209                         }
210                         // We are not a reset node or the previous reset must be on an ancestor of our owner renderer
211                         // hence we must be a child of that reset counter.
212                         parent = currentCounter;
213                         // In some cases renders can be reparented (ex. nodes inside a table but not in a column or row).
214                         // In these cases the identified previousSibling will be invalid as its parent is different from
215                         // our identified parent.
216                         if (previousSiblingProtector->parent() != currentCounter)
217                             previousSiblingProtector = 0;
218
219                         previousSibling = previousSiblingProtector.get();
220                         return true;
221                     }
222                     // CurrentCounter, the counter at the EndSearchRenderer, is not reset.
223                     if (!isReset || !areRenderersElementsSiblings(currentRenderer, counterOwner)) {
224                         // If the node we are placing is not reset or we have found a counter that is attached
225                         // to an ancestor of the placed counter's owner renderer we know we are a sibling of that node.
226                         if (currentCounter->parent() != previousSiblingProtector->parent())
227                             return false;
228
229                         parent = currentCounter->parent();
230                         previousSibling = previousSiblingProtector.get();
231                         return true;
232                     }
233                 } else { 
234                     // We are at the potential end of the search, but we had no previous sibling candidate
235                     // In this case we follow pretty much the same logic as above but no ASSERTs about 
236                     // previousSibling, and when we are a sibling of the end counter we must set previousSibling
237                     // to currentCounter.
238                     if (currentCounter->actsAsReset()) {
239                         if (isReset && areRenderersElementsSiblings(currentRenderer, counterOwner)) {
240                             parent = currentCounter->parent();
241                             previousSibling = currentCounter;
242                             return parent;
243                         }
244                         parent = currentCounter;
245                         previousSibling = previousSiblingProtector.get();
246                         return true;
247                     }
248                     if (!isReset || !areRenderersElementsSiblings(currentRenderer, counterOwner)) {
249                         parent = currentCounter->parent();
250                         previousSibling = currentCounter;
251                         return true;
252                     }
253                     previousSiblingProtector = currentCounter;
254                 }
255             }
256             // We come here if the previous sibling or parent of our owner renderer had no
257             // good counter, or we are a reset node and the counter on the previous sibling
258             // of our owner renderer was not a reset counter.
259             // Set a new goal for the end of the search.
260             searchEndRenderer = previousSiblingOrParent(currentRenderer);
261         } else {
262             // We are searching descendants of a previous sibling of the renderer that the
263             // counter being placed is attached to.
264             if (currentCounter) {
265                 // We found a suitable counter.
266                 if (previousSiblingProtector) {
267                     // Since we had a suitable previous counter before, we should only consider this one as our 
268                     // previousSibling if it is a reset counter and hence the current previousSibling is its child.
269                     if (currentCounter->actsAsReset()) {
270                         previousSiblingProtector = currentCounter;
271                         // We are no longer interested in previous siblings of the currentRenderer or their children
272                         // as counters they may have attached cannot be the previous sibling of the counter we are placing.
273                         currentRenderer = parentOrPseudoHostElement(currentRenderer)->renderer();
274                         continue;
275                     }
276                 } else
277                     previousSiblingProtector = currentCounter;
278                 currentRenderer = previousSiblingOrParent(currentRenderer);
279                 continue;
280             }
281         }
282         // This function is designed so that the same test is not done twice in an iteration, except for this one
283         // which may be done twice in some cases. Rearranging the decision points though, to accommodate this 
284         // performance improvement would create more code duplication than is worthwhile in my oppinion and may further
285         // impede the readability of this already complex algorithm.
286         if (previousSiblingProtector)
287             currentRenderer = previousSiblingOrParent(currentRenderer);
288         else
289             currentRenderer = previousInPreOrder(currentRenderer);
290     }
291     return false;
292 }
293
294 static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& identifier, bool alwaysCreateCounter)
295 {
296     ASSERT(object);
297
298     // Real text nodes don't have their own style so they can't have counters.
299     // We can't even look at their styles or we'll see extra resets and increments!
300     if (object->isText())
301         return nullptr;
302
303     RenderElement* element = toRenderElement(object);
304
305     if (element->hasCounterNodeMap()) {
306         if (CounterMap* nodeMap = counterMaps().get(element)) {
307             if (CounterNode* node = nodeMap->get(identifier))
308                 return node;
309         }
310     }
311
312     bool isReset = false;
313     int value = 0;
314     if (!planCounter(element, identifier, isReset, value) && !alwaysCreateCounter)
315         return nullptr;
316
317     RefPtr<CounterNode> newParent = 0;
318     RefPtr<CounterNode> newPreviousSibling = 0;
319     RefPtr<CounterNode> newNode = CounterNode::create(element, isReset, value);
320     if (findPlaceForCounter(element, identifier, isReset, newParent, newPreviousSibling))
321         newParent->insertAfter(newNode.get(), newPreviousSibling.get(), identifier);
322     CounterMap* nodeMap;
323     if (element->hasCounterNodeMap())
324         nodeMap = counterMaps().get(element);
325     else {
326         nodeMap = new CounterMap;
327         counterMaps().set(element, std::unique_ptr<CounterMap>(nodeMap));
328         element->setHasCounterNodeMap(true);
329     }
330     nodeMap->set(identifier, newNode);
331     if (newNode->parent())
332         return newNode.get();
333     // Checking if some nodes that were previously counter tree root nodes
334     // should become children of this node now.
335     CounterMaps& maps = counterMaps();
336     Element* stayWithin = parentOrPseudoHostElement(element);
337     bool skipDescendants;
338     for (RenderElement* currentRenderer = nextInPreOrder(element, stayWithin); currentRenderer; currentRenderer = nextInPreOrder(currentRenderer, stayWithin, skipDescendants)) {
339         skipDescendants = false;
340         if (!currentRenderer->hasCounterNodeMap())
341             continue;
342         CounterNode* currentCounter = maps.get(currentRenderer)->get(identifier);
343         if (!currentCounter)
344             continue;
345         skipDescendants = true;
346         if (currentCounter->parent())
347             continue;
348         if (stayWithin == parentOrPseudoHostElement(currentRenderer) && currentCounter->hasResetType())
349             break;
350         newNode->insertAfter(currentCounter, newNode->lastChild(), identifier);
351     }
352     return newNode.get();
353 }
354
355 RenderCounter::RenderCounter(Document& document, const CounterContent& counter)
356     : RenderText(document, emptyString())
357     , m_counter(counter)
358     , m_counterNode(nullptr)
359     , m_nextForSameCounter(0)
360 {
361     view().addRenderCounter();
362 }
363
364 RenderCounter::~RenderCounter()
365 {
366     if (m_counterNode) {
367         m_counterNode->removeRenderer(this);
368         ASSERT(!m_counterNode);
369     }
370 }
371
372 void RenderCounter::willBeDestroyed()
373 {
374     view().removeRenderCounter();
375     RenderText::willBeDestroyed();
376 }
377
378 const char* RenderCounter::renderName() const
379 {
380     return "RenderCounter";
381 }
382
383 bool RenderCounter::isCounter() const
384 {
385     return true;
386 }
387
388 String RenderCounter::originalText() const
389 {
390     if (!m_counterNode) {
391         RenderElement* beforeAfterContainer = parent();
392         while (true) {
393             if (!beforeAfterContainer)
394                 return String();
395             if (!beforeAfterContainer->isAnonymous() && !beforeAfterContainer->isPseudoElement())
396                 return String(); // RenderCounters are restricted to before and after pseudo elements
397             PseudoId containerStyle = beforeAfterContainer->style().styleType();
398             if ((containerStyle == BEFORE) || (containerStyle == AFTER))
399                 break;
400             beforeAfterContainer = beforeAfterContainer->parent();
401         }
402         makeCounterNode(beforeAfterContainer, m_counter.identifier(), true)->addRenderer(const_cast<RenderCounter*>(this));
403         ASSERT(m_counterNode);
404     }
405     CounterNode* child = m_counterNode;
406     int value = child->actsAsReset() ? child->value() : child->countInParent();
407
408     String text = listMarkerText(m_counter.listStyle(), value);
409
410     if (!m_counter.separator().isNull()) {
411         if (!child->actsAsReset())
412             child = child->parent();
413         while (CounterNode* parent = child->parent()) {
414             text = listMarkerText(m_counter.listStyle(), child->countInParent())
415                 + m_counter.separator() + text;
416             child = parent;
417         }
418     }
419
420     return text;
421 }
422
423 void RenderCounter::updateCounter()
424 {
425     computePreferredLogicalWidths(0);
426 }
427
428 void RenderCounter::computePreferredLogicalWidths(float lead)
429 {
430 #ifndef NDEBUG
431     // FIXME: We shouldn't be modifying the tree in computePreferredLogicalWidths.
432     // Instead, we should properly hook the appropriate changes in the DOM and modify
433     // the render tree then. When that's done, we also won't need to override
434     // computePreferredLogicalWidths at all.
435     // https://bugs.webkit.org/show_bug.cgi?id=104829
436     SetLayoutNeededForbiddenScope layoutForbiddenScope(this, false);
437 #endif
438
439     setTextInternal(originalText());
440
441     RenderText::computePreferredLogicalWidths(lead);
442 }
443
444 void RenderCounter::invalidate()
445 {
446     m_counterNode->removeRenderer(this);
447     ASSERT(!m_counterNode);
448     if (documentBeingDestroyed())
449         return;
450     setNeedsLayoutAndPrefWidthsRecalc();
451 }
452
453 static void destroyCounterNodeWithoutMapRemoval(const AtomicString& identifier, CounterNode* node)
454 {
455     CounterNode* previous;
456     for (RefPtr<CounterNode> child = node->lastDescendant(); child && child != node; child = previous) {
457         previous = child->previousInPreOrder();
458         child->parent()->removeChild(child.get());
459         ASSERT(counterMaps().get(child->owner())->get(identifier) == child);
460         counterMaps().get(child->owner())->remove(identifier);
461     }
462     if (CounterNode* parent = node->parent())
463         parent->removeChild(node);
464 }
465
466 void RenderCounter::destroyCounterNodes(RenderObject* owner)
467 {
468     CounterMaps& maps = counterMaps();
469     CounterMaps::iterator mapsIterator = maps.find(owner);
470     if (mapsIterator == maps.end())
471         return;
472     CounterMap* map = mapsIterator->value.get();
473     CounterMap::const_iterator end = map->end();
474     for (CounterMap::const_iterator it = map->begin(); it != end; ++it) {
475         destroyCounterNodeWithoutMapRemoval(it->key, it->value.get());
476     }
477     maps.remove(mapsIterator);
478     owner->setHasCounterNodeMap(false);
479 }
480
481 void RenderCounter::destroyCounterNode(RenderObject* owner, const AtomicString& identifier)
482 {
483     CounterMap* map = counterMaps().get(owner);
484     if (!map)
485         return;
486     CounterMap::iterator mapIterator = map->find(identifier);
487     if (mapIterator == map->end())
488         return;
489     destroyCounterNodeWithoutMapRemoval(identifier, mapIterator->value.get());
490     map->remove(mapIterator);
491     // We do not delete "map" here even if empty because we expect to reuse
492     // it soon. In order for a renderer to lose all its counters permanently,
493     // a style change for the renderer involving removal of all counter
494     // directives must occur, in which case, RenderCounter::destroyCounterNodes()
495     // must be called.
496     // The destruction of the Renderer (possibly caused by the removal of its 
497     // associated DOM node) is the other case that leads to the permanent
498     // destruction of all counters attached to a Renderer. In this case
499     // RenderCounter::destroyCounterNodes() must be and is now called, too.
500     // RenderCounter::destroyCounterNodes() handles destruction of the counter
501     // map associated with a renderer, so there is no risk in leaking the map.
502 }
503
504 void RenderCounter::rendererRemovedFromTree(RenderObject& renderer)
505 {
506     if (!renderer.view().hasRenderCounters())
507         return;
508     RenderObject* currentRenderer = renderer.lastLeafChild();
509     if (!currentRenderer)
510         currentRenderer = &renderer;
511     while (true) {
512         destroyCounterNodes(currentRenderer);
513         if (currentRenderer == &renderer)
514             break;
515         currentRenderer = currentRenderer->previousInPreOrder();
516     }
517 }
518
519 static void updateCounters(RenderObject* renderer)
520 {
521     const CounterDirectiveMap* directiveMap = renderer->style().counterDirectives();
522     if (!directiveMap)
523         return;
524     CounterDirectiveMap::const_iterator end = directiveMap->end();
525     if (!renderer->hasCounterNodeMap()) {
526         for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it)
527             makeCounterNode(renderer, it->key, false);
528         return;
529     }
530     CounterMap* counterMap = counterMaps().get(renderer);
531     ASSERT(counterMap);
532     for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) {
533         RefPtr<CounterNode> node = counterMap->get(it->key);
534         if (!node) {
535             makeCounterNode(renderer, it->key, false);
536             continue;
537         }
538         RefPtr<CounterNode> newParent = 0;
539         RefPtr<CounterNode> newPreviousSibling = 0;
540         
541         findPlaceForCounter(renderer, it->key, node->hasResetType(), newParent, newPreviousSibling);
542         if (node != counterMap->get(it->key))
543             continue;
544         CounterNode* parent = node->parent();
545         if (newParent == parent && newPreviousSibling == node->previousSibling())
546             continue;
547         if (parent)
548             parent->removeChild(node.get());
549         if (newParent)
550             newParent->insertAfter(node.get(), newPreviousSibling.get(), it->key);
551     }
552 }
553
554 void RenderCounter::rendererSubtreeAttached(RenderObject* renderer)
555 {
556     if (!renderer->view().hasRenderCounters())
557         return;
558     Node* node = renderer->node();
559     if (node && !node->isPseudoElement())
560         node = node->parentNode();
561     else
562         node = renderer->generatingNode();
563     if (node && !node->renderer())
564         return; // No need to update if the parent is not attached yet
565     for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer))
566         updateCounters(descendant);
567 }
568
569 void RenderCounter::rendererStyleChanged(RenderObject* renderer, const RenderStyle* oldStyle, const RenderStyle* newStyle)
570 {
571     Node* node = renderer->generatingNode();
572     if (!node || !node->renderer())
573         return; // cannot have generated content or if it can have, it will be handled during attaching
574     const CounterDirectiveMap* newCounterDirectives;
575     const CounterDirectiveMap* oldCounterDirectives;
576     if (oldStyle && (oldCounterDirectives = oldStyle->counterDirectives())) {
577         if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) {
578             CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end();
579             CounterDirectiveMap::const_iterator oldMapEnd = oldCounterDirectives->end();
580             for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) {
581                 CounterDirectiveMap::const_iterator oldMapIt = oldCounterDirectives->find(it->key);
582                 if (oldMapIt != oldMapEnd) {
583                     if (oldMapIt->value == it->value)
584                         continue;
585                     RenderCounter::destroyCounterNode(renderer, it->key);
586                 }
587                 // We must create this node here, because the changed node may be a node with no display such as
588                 // as those created by the increment or reset directives and the re-layout that will happen will
589                 // not catch the change if the node had no children.
590                 makeCounterNode(renderer, it->key, false);
591             }
592             // Destroying old counters that do not exist in the new counterDirective map.
593             for (CounterDirectiveMap::const_iterator it = oldCounterDirectives->begin(); it !=oldMapEnd; ++it) {
594                 if (!newCounterDirectives->contains(it->key))
595                     RenderCounter::destroyCounterNode(renderer, it->key);
596             }
597         } else {
598             if (renderer->hasCounterNodeMap())
599                 RenderCounter::destroyCounterNodes(renderer);
600         }
601     } else if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) {
602         CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end();
603         for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) {
604             // We must create this node here, because the added node may be a node with no display such as
605             // as those created by the increment or reset directives and the re-layout that will happen will
606             // not catch the change if the node had no children.
607             makeCounterNode(renderer, it->key, false);
608         }
609     }
610 }
611
612 } // namespace WebCore
613
614 #ifndef NDEBUG
615
616 void showCounterRendererTree(const WebCore::RenderObject* renderer, const char* counterName)
617 {
618     if (!renderer)
619         return;
620     const WebCore::RenderObject* root = renderer;
621     while (root->parent())
622         root = root->parent();
623
624     AtomicString identifier(counterName);
625     for (const WebCore::RenderObject* current = root; current; current = current->nextInPreOrder()) {
626         fprintf(stderr, "%c", (current == renderer) ? '*' : ' ');
627         for (const WebCore::RenderObject* parent = current; parent && parent != root; parent = parent->parent())
628             fprintf(stderr, "    ");
629         fprintf(stderr, "%p N:%p P:%p PS:%p NS:%p C:%p\n",
630             current, current->node(), current->parent(), current->previousSibling(),
631             current->nextSibling(), current->hasCounterNodeMap() ?
632             counterName ? WebCore::counterMaps().get(current)->get(identifier) : (WebCore::CounterNode*)1 : (WebCore::CounterNode*)0);
633     }
634     fflush(stderr);
635 }
636
637 #endif // NDEBUG