2 * Copyright (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com)
3 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
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.
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.
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.
23 #include "RenderCounter.h"
25 #include "CounterNode.h"
28 #include "HTMLNames.h"
29 #include "HTMLOListElement.h"
30 #include "RenderListItem.h"
31 #include "RenderListMarker.h"
32 #include "RenderStyle.h"
33 #include <wtf/StdLibExtras.h>
41 using namespace HTMLNames;
43 typedef HashMap<RefPtr<AtomicStringImpl>, RefPtr<CounterNode> > CounterMap;
44 typedef HashMap<const RenderObject*, OwnPtr<CounterMap> > CounterMaps;
46 static CounterNode* makeCounterNode(RenderObject*, const AtomicString& identifier, bool alwaysCreateCounter);
48 static CounterMaps& counterMaps()
50 DEFINE_STATIC_LOCAL(CounterMaps, staticCounterMaps, ());
51 return staticCounterMaps;
54 static RenderObject* rendererOfAfterPseudoElement(RenderObject* renderer)
56 RenderObject* lastContinuation = renderer;
57 while (RenderObject* continuation = lastContinuation->virtualContinuation())
58 lastContinuation = continuation;
59 return lastContinuation->afterPseudoElementRenderer();
62 // This function processes the renderer tree in the order of the DOM tree
63 // including pseudo elements as defined in CSS 2.1.
64 // Anonymous renderers are skipped except for those representing pseudo elements.
65 static RenderObject* previousInPreOrder(const RenderObject* object)
69 switch (object->style()->styleType()) {
71 ASSERT(!object->isAnonymous());
72 parent = toElement(object->node());
73 sibling = parent->previousElementSibling();
74 parent = parent->parentElement();
77 return object->generatingNode()->renderer(); // It is always the generating node's renderer
79 parent = toElement(object->generatingNode());
80 sibling = parent->lastElementChild();
87 if (RenderObject* renderer = sibling->renderer()) {
88 if (RenderObject* after = rendererOfAfterPseudoElement(renderer))
91 sibling = sibling->lastElementChild();
93 if (RenderObject* before = renderer->beforePseudoElementRenderer())
98 sibling = sibling->previousElementSibling();
102 RenderObject* renderer = parent->renderer(); // Should never be null
103 if (RenderObject* before = renderer->beforePseudoElementRenderer())
108 // This function processes the renderer tree in the order of the DOM tree
109 // including pseudo elements as defined in CSS 2.1.
110 // Anonymous renderers are skipped except for those representing pseudo elements.
111 static RenderObject* previousSiblingOrParent(const RenderObject* object)
115 switch (object->style()->styleType()) {
117 ASSERT(!object->isAnonymous());
118 parent = toElement(object->node());
119 sibling = parent->previousElementSibling();
120 parent = parent->parentElement();
123 return object->generatingNode()->renderer(); // It is always the generating node's renderer
125 parent = toElement(object->generatingNode());
126 sibling = parent->lastElementChild();
129 ASSERT_NOT_REACHED();
133 if (RenderObject* renderer = sibling->renderer()) // This skips invisible nodes
135 sibling = sibling->previousElementSibling();
138 RenderObject* renderer = parent->renderer();
139 if (RenderObject* before = renderer->virtualChildren()->beforePseudoElementRenderer(renderer))
146 static Element* parentElement(RenderObject* object)
148 switch (object->style()->styleType()) {
150 ASSERT(!object->isAnonymous());
151 return toElement(object->node())->parentElement();
154 return toElement(object->generatingNode());
156 ASSERT_NOT_REACHED();
161 static inline bool areRenderersElementsSiblings(RenderObject* first, RenderObject* second)
163 return parentElement(first) == parentElement(second);
166 // This function processes the renderer tree in the order of the DOM tree
167 // including pseudo elements as defined in CSS 2.1.
168 // Anonymous renderers are skipped except for those representing pseudo elements.
169 static RenderObject* nextInPreOrder(const RenderObject* object, const Element* stayWithin, bool skipDescendants = false)
173 RenderObject* result;
174 self = toElement(object->generatingNode());
177 switch (object->style()->styleType()) {
179 ASSERT(!object->isAnonymous());
180 result = object->beforePseudoElementRenderer();
189 ASSERT_NOT_REACHED();
192 child = self->firstElementChild();
195 result = child->renderer();
198 child = child->nextElementSibling();
200 result = rendererOfAfterPseudoElement(self->renderer());
204 if (self == stayWithin)
206 child = self->nextElementSibling();
207 self = self->parentElement();
209 ASSERT(!child); // We can only reach this if we are searching beyond the root element
210 return 0; // which cannot have siblings
215 static bool planCounter(RenderObject* object, const AtomicString& identifier, bool& isReset, int& value)
219 // Real text nodes don't have their own style so they can't have counters.
220 // We can't even look at their styles or we'll see extra resets and increments!
221 if (object->isText() && !object->isBR())
223 Node* generatingNode = object->generatingNode();
224 // We must have a generating node or else we cannot have a counter.
227 RenderStyle* style = object->style();
230 switch (style->styleType()) {
232 // Sometimes nodes have more then one renderer. Only the first one gets the counter
233 // LayoutTests/http/tests/css/counter-crash.html
234 if (generatingNode->renderer() != object)
241 return false; // Counters are forbidden from all other pseudo elements.
244 if (const CounterDirectiveMap* directivesMap = style->counterDirectives()) {
245 CounterDirectives directives = directivesMap->get(identifier.impl());
246 if (directives.m_reset) {
247 value = directives.m_resetValue;
248 if (directives.m_increment)
249 value += directives.m_incrementValue;
253 if (directives.m_increment) {
254 value = directives.m_incrementValue;
260 if (identifier == "list-item") {
261 if (object->isListItem()) {
262 if (toRenderListItem(object)->hasExplicitValue()) {
263 value = toRenderListItem(object)->explicitValue();
271 if (Node* e = object->node()) {
272 if (e->hasTagName(olTag)) {
273 value = static_cast<HTMLOListElement*>(e)->start();
277 if (e->hasTagName(ulTag) || e->hasTagName(menuTag) || e->hasTagName(dirTag)) {
288 // - Finds the insertion point for the counter described by counterOwner, isReset and
289 // identifier in the CounterNode tree for identifier and sets parent and
290 // previousSibling accordingly.
291 // - The function returns true if the counter whose insertion point is searched is NOT
292 // the root of the tree.
293 // - The root of the tree is a counter reference that is not in the scope of any other
294 // counter with the same identifier.
295 // - All the counter references with the same identifier as this one that are in
296 // children or subsequent siblings of the renderer that owns the root of the tree
297 // form the rest of of the nodes of the tree.
298 // - The root of the tree is always a reset type reference.
299 // - A subtree rooted at any reset node in the tree is equivalent to all counter
300 // references that are in the scope of the counter or nested counter defined by that
302 // - Non-reset CounterNodes cannot have descendants.
304 static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& identifier, bool isReset, RefPtr<CounterNode>& parent, RefPtr<CounterNode>& previousSibling)
306 // We cannot stop searching for counters with the same identifier before we also
307 // check this renderer, because it may affect the positioning in the tree of our counter.
308 RenderObject* searchEndRenderer = previousSiblingOrParent(counterOwner);
309 // We check renderers in preOrder from the renderer that our counter is attached to
310 // towards the begining of the document for counters with the same identifier as the one
311 // we are trying to find a place for. This is the next renderer to be checked.
312 RenderObject* currentRenderer = previousInPreOrder(counterOwner);
314 RefPtr<CounterNode> previousSiblingProtector = 0;
316 while (currentRenderer) {
317 CounterNode* currentCounter = makeCounterNode(currentRenderer, identifier, false);
318 if (searchEndRenderer == currentRenderer) {
319 // We may be at the end of our search.
320 if (currentCounter) {
321 // We have a suitable counter on the EndSearchRenderer.
322 if (previousSiblingProtector) { // But we already found another counter that we come after.
323 if (currentCounter->actsAsReset()) {
324 // We found a reset counter that is on a renderer that is a sibling of ours or a parent.
325 if (isReset && areRenderersElementsSiblings(currentRenderer, counterOwner)) {
326 // We are also a reset counter and the previous reset was on a sibling renderer
327 // hence we are the next sibling of that counter if that reset is not a root or
328 // we are a root node if that reset is a root.
329 parent = currentCounter->parent();
330 previousSibling = parent ? currentCounter : 0;
333 // We are not a reset node or the previous reset must be on an ancestor of our owner renderer
334 // hence we must be a child of that reset counter.
335 parent = currentCounter;
336 // In some cases renders can be reparented (ex. nodes inside a table but not in a column or row).
337 // In these cases the identified previousSibling will be invalid as its parent is different from
338 // our identified parent.
339 if (previousSiblingProtector->parent() != currentCounter)
340 previousSiblingProtector = 0;
342 previousSibling = previousSiblingProtector.get();
345 // CurrentCounter, the counter at the EndSearchRenderer, is not reset.
346 if (!isReset || !areRenderersElementsSiblings(currentRenderer, counterOwner)) {
347 // If the node we are placing is not reset or we have found a counter that is attached
348 // to an ancestor of the placed counter's owner renderer we know we are a sibling of that node.
349 if (currentCounter->parent() != previousSiblingProtector->parent())
352 parent = currentCounter->parent();
353 previousSibling = previousSiblingProtector.get();
357 // We are at the potential end of the search, but we had no previous sibling candidate
358 // In this case we follow pretty much the same logic as above but no ASSERTs about
359 // previousSibling, and when we are a sibling of the end counter we must set previousSibling
360 // to currentCounter.
361 if (currentCounter->actsAsReset()) {
362 if (isReset && areRenderersElementsSiblings(currentRenderer, counterOwner)) {
363 parent = currentCounter->parent();
364 previousSibling = currentCounter;
367 parent = currentCounter;
368 previousSibling = previousSiblingProtector.get();
371 if (!isReset || !areRenderersElementsSiblings(currentRenderer, counterOwner)) {
372 parent = currentCounter->parent();
373 previousSibling = currentCounter;
376 previousSiblingProtector = currentCounter;
379 // We come here if the previous sibling or parent of our owner renderer had no
380 // good counter, or we are a reset node and the counter on the previous sibling
381 // of our owner renderer was not a reset counter.
382 // Set a new goal for the end of the search.
383 searchEndRenderer = previousSiblingOrParent(currentRenderer);
385 // We are searching descendants of a previous sibling of the renderer that the
386 // counter being placed is attached to.
387 if (currentCounter) {
388 // We found a suitable counter.
389 if (previousSiblingProtector) {
390 // Since we had a suitable previous counter before, we should only consider this one as our
391 // previousSibling if it is a reset counter and hence the current previousSibling is its child.
392 if (currentCounter->actsAsReset()) {
393 previousSiblingProtector = currentCounter;
394 // We are no longer interested in previous siblings of the currentRenderer or their children
395 // as counters they may have attached cannot be the previous sibling of the counter we are placing.
396 currentRenderer = parentElement(currentRenderer)->renderer();
400 previousSiblingProtector = currentCounter;
401 currentRenderer = previousSiblingOrParent(currentRenderer);
405 // This function is designed so that the same test is not done twice in an iteration, except for this one
406 // which may be done twice in some cases. Rearranging the decision points though, to accommodate this
407 // performance improvement would create more code duplication than is worthwhile in my oppinion and may further
408 // impede the readability of this already complex algorithm.
409 if (previousSiblingProtector)
410 currentRenderer = previousSiblingOrParent(currentRenderer);
412 currentRenderer = previousInPreOrder(currentRenderer);
417 static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& identifier, bool alwaysCreateCounter)
421 if (object->hasCounterNodeMap()) {
422 if (CounterMap* nodeMap = counterMaps().get(object)) {
423 if (CounterNode* node = nodeMap->get(identifier.impl()).get())
428 bool isReset = false;
430 if (!planCounter(object, identifier, isReset, value) && !alwaysCreateCounter)
433 RefPtr<CounterNode> newParent = 0;
434 RefPtr<CounterNode> newPreviousSibling = 0;
435 RefPtr<CounterNode> newNode = CounterNode::create(object, isReset, value);
436 if (findPlaceForCounter(object, identifier, isReset, newParent, newPreviousSibling))
437 newParent->insertAfter(newNode.get(), newPreviousSibling.get(), identifier);
439 if (object->hasCounterNodeMap())
440 nodeMap = counterMaps().get(object);
442 nodeMap = new CounterMap;
443 counterMaps().set(object, adoptPtr(nodeMap));
444 object->setHasCounterNodeMap(true);
446 nodeMap->set(identifier.impl(), newNode);
447 if (newNode->parent())
448 return newNode.get();
449 // Checking if some nodes that were previously counter tree root nodes
450 // should become children of this node now.
451 CounterMaps& maps = counterMaps();
452 Element* stayWithin = parentElement(object);
453 bool skipDescendants;
454 for (RenderObject* currentRenderer = nextInPreOrder(object, stayWithin); currentRenderer; currentRenderer = nextInPreOrder(currentRenderer, stayWithin, skipDescendants)) {
455 skipDescendants = false;
456 if (!currentRenderer->hasCounterNodeMap())
458 CounterNode* currentCounter = maps.get(currentRenderer)->get(identifier.impl()).get();
461 skipDescendants = true;
462 if (currentCounter->parent())
464 if (stayWithin == parentElement(currentRenderer) && currentCounter->hasResetType())
466 newNode->insertAfter(currentCounter, newNode->lastChild(), identifier);
468 return newNode.get();
471 RenderCounter::RenderCounter(Document* node, const CounterContent& counter)
472 : RenderText(node, StringImpl::empty())
475 , m_nextForSameCounter(0)
477 view()->addRenderCounter();
480 RenderCounter::~RenderCounter()
483 m_counterNode->removeRenderer(this);
484 ASSERT(!m_counterNode);
488 void RenderCounter::willBeDestroyed()
491 view()->removeRenderCounter();
492 RenderText::willBeDestroyed();
495 const char* RenderCounter::renderName() const
497 return "RenderCounter";
500 bool RenderCounter::isCounter() const
505 PassRefPtr<StringImpl> RenderCounter::originalText() const
507 if (!m_counterNode) {
508 RenderObject* beforeAfterContainer = parent();
510 if (!beforeAfterContainer)
512 if (!beforeAfterContainer->isAnonymous())
513 return 0; // RenderCounters are restricted to before and after pseudo elements
514 PseudoId containerStyle = beforeAfterContainer->style()->styleType();
515 if ((containerStyle == BEFORE) || (containerStyle == AFTER))
517 beforeAfterContainer = beforeAfterContainer->parent();
519 makeCounterNode(beforeAfterContainer, m_counter.identifier(), true)->addRenderer(const_cast<RenderCounter*>(this));
520 ASSERT(m_counterNode);
522 CounterNode* child = m_counterNode;
523 int value = child->actsAsReset() ? child->value() : child->countInParent();
525 String text = listMarkerText(m_counter.listStyle(), value);
527 if (!m_counter.separator().isNull()) {
528 if (!child->actsAsReset())
529 child = child->parent();
530 while (CounterNode* parent = child->parent()) {
531 text = listMarkerText(m_counter.listStyle(), child->countInParent())
532 + m_counter.separator() + text;
540 void RenderCounter::computePreferredLogicalWidths(float lead)
542 setTextInternal(originalText());
543 RenderText::computePreferredLogicalWidths(lead);
546 void RenderCounter::invalidate()
548 m_counterNode->removeRenderer(this);
549 ASSERT(!m_counterNode);
550 if (documentBeingDestroyed())
552 setNeedsLayoutAndPrefWidthsRecalc();
555 static void destroyCounterNodeWithoutMapRemoval(const AtomicString& identifier, CounterNode* node)
557 CounterNode* previous;
558 for (RefPtr<CounterNode> child = node->lastDescendant(); child && child != node; child = previous) {
559 previous = child->previousInPreOrder();
560 child->parent()->removeChild(child.get());
561 ASSERT(counterMaps().get(child->owner())->get(identifier.impl()) == child);
562 counterMaps().get(child->owner())->remove(identifier.impl());
564 if (CounterNode* parent = node->parent())
565 parent->removeChild(node);
568 void RenderCounter::destroyCounterNodes(RenderObject* owner)
570 CounterMaps& maps = counterMaps();
571 CounterMaps::iterator mapsIterator = maps.find(owner);
572 if (mapsIterator == maps.end())
574 CounterMap* map = mapsIterator->second.get();
575 CounterMap::const_iterator end = map->end();
576 for (CounterMap::const_iterator it = map->begin(); it != end; ++it) {
577 AtomicString identifier(it->first.get());
578 destroyCounterNodeWithoutMapRemoval(identifier, it->second.get());
580 maps.remove(mapsIterator);
581 owner->setHasCounterNodeMap(false);
584 void RenderCounter::destroyCounterNode(RenderObject* owner, const AtomicString& identifier)
586 CounterMap* map = counterMaps().get(owner);
589 CounterMap::iterator mapIterator = map->find(identifier.impl());
590 if (mapIterator == map->end())
592 destroyCounterNodeWithoutMapRemoval(identifier, mapIterator->second.get());
593 map->remove(mapIterator);
594 // We do not delete "map" here even if empty because we expect to reuse
595 // it soon. In order for a renderer to lose all its counters permanently,
596 // a style change for the renderer involving removal of all counter
597 // directives must occur, in which case, RenderCounter::destroyCounterNodes()
599 // The destruction of the Renderer (possibly caused by the removal of its
600 // associated DOM node) is the other case that leads to the permanent
601 // destruction of all counters attached to a Renderer. In this case
602 // RenderCounter::destroyCounterNodes() must be and is now called, too.
603 // RenderCounter::destroyCounterNodes() handles destruction of the counter
604 // map associated with a renderer, so there is no risk in leaking the map.
607 void RenderCounter::rendererRemovedFromTree(RenderObject* renderer)
609 ASSERT(renderer->view());
610 if (!renderer->view()->hasRenderCounters())
612 RenderObject* currentRenderer = renderer->lastLeafChild();
613 if (!currentRenderer)
614 currentRenderer = renderer;
616 destroyCounterNodes(currentRenderer);
617 if (currentRenderer == renderer)
619 currentRenderer = currentRenderer->previousInPreOrder();
623 static void updateCounters(RenderObject* renderer)
625 ASSERT(renderer->style());
626 const CounterDirectiveMap* directiveMap = renderer->style()->counterDirectives();
629 CounterDirectiveMap::const_iterator end = directiveMap->end();
630 if (!renderer->hasCounterNodeMap()) {
631 for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it)
632 makeCounterNode(renderer, AtomicString(it->first.get()), false);
635 CounterMap* counterMap = counterMaps().get(renderer);
637 for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) {
638 RefPtr<CounterNode> node = counterMap->get(it->first.get());
640 makeCounterNode(renderer, AtomicString(it->first.get()), false);
643 RefPtr<CounterNode> newParent = 0;
644 RefPtr<CounterNode> newPreviousSibling = 0;
646 findPlaceForCounter(renderer, AtomicString(it->first.get()), node->hasResetType(), newParent, newPreviousSibling);
647 if (node != counterMap->get(it->first.get()))
649 CounterNode* parent = node->parent();
650 if (newParent == parent && newPreviousSibling == node->previousSibling())
653 parent->removeChild(node.get());
655 newParent->insertAfter(node.get(), newPreviousSibling.get(), it->first.get());
659 void RenderCounter::rendererSubtreeAttached(RenderObject* renderer)
661 ASSERT(renderer->view());
662 if (!renderer->view()->hasRenderCounters())
664 Node* node = renderer->node();
666 node = node->parentNode();
668 node = renderer->generatingNode();
669 if (node && !node->attached())
670 return; // No need to update if the parent is not attached yet
671 for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer))
672 updateCounters(descendant);
675 void RenderCounter::rendererStyleChanged(RenderObject* renderer, const RenderStyle* oldStyle, const RenderStyle* newStyle)
677 Node* node = renderer->generatingNode();
678 if (!node || !node->attached())
679 return; // cannot have generated content or if it can have, it will be handled during attaching
680 const CounterDirectiveMap* newCounterDirectives;
681 const CounterDirectiveMap* oldCounterDirectives;
682 if (oldStyle && (oldCounterDirectives = oldStyle->counterDirectives())) {
683 if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) {
684 CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end();
685 CounterDirectiveMap::const_iterator oldMapEnd = oldCounterDirectives->end();
686 for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) {
687 CounterDirectiveMap::const_iterator oldMapIt = oldCounterDirectives->find(it->first);
688 if (oldMapIt != oldMapEnd) {
689 if (oldMapIt->second == it->second)
691 RenderCounter::destroyCounterNode(renderer, it->first.get());
693 // We must create this node here, because the changed node may be a node with no display such as
694 // as those created by the increment or reset directives and the re-layout that will happen will
695 // not catch the change if the node had no children.
696 makeCounterNode(renderer, it->first.get(), false);
698 // Destroying old counters that do not exist in the new counterDirective map.
699 for (CounterDirectiveMap::const_iterator it = oldCounterDirectives->begin(); it !=oldMapEnd; ++it) {
700 if (!newCounterDirectives->contains(it->first))
701 RenderCounter::destroyCounterNode(renderer, it->first.get());
704 if (renderer->hasCounterNodeMap())
705 RenderCounter::destroyCounterNodes(renderer);
707 } else if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) {
708 CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end();
709 for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) {
710 // We must create this node here, because the added node may be a node with no display such as
711 // as those created by the increment or reset directives and the re-layout that will happen will
712 // not catch the change if the node had no children.
713 makeCounterNode(renderer, it->first.get(), false);
718 } // namespace WebCore
722 void showCounterRendererTree(const WebCore::RenderObject* renderer, const char* counterName)
726 const WebCore::RenderObject* root = renderer;
727 while (root->parent())
728 root = root->parent();
730 AtomicString identifier(counterName);
731 for (const WebCore::RenderObject* current = root; current; current = current->nextInPreOrder()) {
732 fprintf(stderr, "%c", (current == renderer) ? '*' : ' ');
733 for (const WebCore::RenderObject* parent = current; parent && parent != root; parent = parent->parent())
734 fprintf(stderr, " ");
735 fprintf(stderr, "%p N:%p P:%p PS:%p NS:%p C:%p\n",
736 current, current->node(), current->parent(), current->previousSibling(),
737 current->nextSibling(), current->hasCounterNodeMap() ?
738 counterName ? WebCore::counterMaps().get(current)->get(identifier.impl()).get() : (WebCore::CounterNode*)1 : (WebCore::CounterNode*)0);