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