1 /** 2 * Copyright (C) 2004 Allan Sandfeld Jensen (kde (at) 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 "HTMLNames.h" 29 #include "HTMLOListElement.h" 30 #include "RenderListItem.h" 31 #include "RenderListMarker.h" 32 #include "RenderStyle.h" 33 #include <wtf/StdLibExtras.h> 34 35 namespace WebCore { 36 37 using namespace HTMLNames; 38 39 typedef HashMap<RefPtr<AtomicStringImpl>, RefPtr<CounterNode> > CounterMap; 40 typedef HashMap<const RenderObject*, CounterMap*> CounterMaps; 41 42 static CounterNode* makeCounterNode(RenderObject*, const AtomicString& identifier, bool alwaysCreateCounter); 43 44 static CounterMaps& counterMaps() 45 { 46 DEFINE_STATIC_LOCAL(CounterMaps, staticCounterMaps, ()); 47 return staticCounterMaps; 48 } 49 50 // This function processes the renderer tree in the order of the DOM tree 51 // including pseudo elements as defined in CSS 2.1. 52 // Anonymous renderers are skipped except for those representing pseudo elements. 53 static RenderObject* previousInPreOrder(const RenderObject* object) 54 { 55 Element* parent; 56 Element* sibling; 57 switch (object->style()->styleType()) { 58 case NOPSEUDO: 59 ASSERT(!object->isAnonymous()); 60 parent = toElement(object->node()); 61 sibling = parent->previousElementSibling(); 62 parent = parent->parentElement(); 63 break; 64 case BEFORE: 65 return object->generatingNode()->renderer(); // It is always the generating node's renderer 66 case AFTER: 67 parent = toElement(object->generatingNode()); 68 sibling = parent->lastElementChild(); 69 break; 70 default: 71 ASSERT_NOT_REACHED(); 72 return 0; 73 } 74 while (sibling) { 75 if (RenderObject* renderer = sibling->renderer()) { 76 if (RenderObject* after = renderer->afterPseudoElementRenderer()) 77 return after; 78 parent = sibling; 79 sibling = sibling->lastElementChild(); 80 if (!sibling) { 81 if (RenderObject* before = renderer->beforePseudoElementRenderer()) 82 return before; 83 return renderer; 84 } 85 } else 86 sibling = sibling->previousElementSibling(); 87 } 88 if (!parent) 89 return 0; 90 RenderObject* renderer = parent->renderer(); // Should never be null 91 if (RenderObject* before = renderer->beforePseudoElementRenderer()) 92 return before; 93 return renderer; 94 } 95 96 // This function processes the renderer tree in the order of the DOM tree 97 // including pseudo elements as defined in CSS 2.1. 98 // Anonymous renderers are skipped except for those representing pseudo elements. 99 static RenderObject* previousSiblingOrParent(const RenderObject* object) 100 { 101 Element* parent; 102 Element* sibling; 103 switch (object->style()->styleType()) { 104 case NOPSEUDO: 105 ASSERT(!object->isAnonymous()); 106 parent = toElement(object->node()); 107 sibling = parent->previousElementSibling(); 108 parent = parent->parentElement(); 109 break; 110 case BEFORE: 111 return object->generatingNode()->renderer(); // It is always the generating node's renderer 112 case AFTER: 113 parent = toElement(object->generatingNode()); 114 sibling = parent->lastElementChild(); 115 break; 116 default: 117 ASSERT_NOT_REACHED(); 118 return 0; 119 } 120 while (sibling) { 121 if (RenderObject* renderer = sibling->renderer()) // This skips invisible nodes 122 return renderer; 123 sibling = sibling->previousElementSibling(); 124 } 125 if (parent) { 126 RenderObject* renderer = parent->renderer(); 127 if (RenderObject* before = renderer->virtualChildren()->beforePseudoElementRenderer(renderer)) 128 return before; 129 return renderer; 130 } 131 return 0; 132 } 133 134 static Element* parentElement(RenderObject* object) 135 { 136 switch (object->style()->styleType()) { 137 case NOPSEUDO: 138 ASSERT(!object->isAnonymous()); 139 return toElement(object->node())->parentElement(); 140 case BEFORE: 141 case AFTER: 142 return toElement(object->generatingNode()); 143 default: 144 ASSERT_NOT_REACHED(); 145 return 0; 146 } 147 } 148 149 static inline bool areRenderersElementsSiblings(RenderObject* first, RenderObject* second) 150 { 151 return parentElement(first) == parentElement(second); 152 } 153 154 // This function processes the renderer tree in the order of the DOM tree 155 // including pseudo elements as defined in CSS 2.1. 156 // Anonymous renderers are skipped except for those representing pseudo elements. 157 static RenderObject* nextInPreOrder(const RenderObject* object, const Element* stayWithin, bool skipDescendants = false) 158 { 159 Element* self; 160 Element* child; 161 RenderObject* result; 162 self = toElement(object->generatingNode()); 163 if (skipDescendants) 164 goto nextsibling; 165 switch (object->style()->styleType()) { 166 case NOPSEUDO: 167 ASSERT(!object->isAnonymous()); 168 result = object->beforePseudoElementRenderer(); 169 if (result) 170 return result; 171 break; 172 case BEFORE: 173 break; 174 case AFTER: 175 goto nextsibling; 176 default: 177 ASSERT_NOT_REACHED(); 178 return 0; 179 } 180 child = self->firstElementChild(); 181 while (true) { 182 while (child) { 183 result = child->renderer(); 184 if (result) 185 return result; 186 child = child->nextElementSibling(); 187 } 188 result = self->renderer()->afterPseudoElementRenderer(); 189 if (result) 190 return result; 191 nextsibling: 192 if (self == stayWithin) 193 return 0; 194 child = self->nextElementSibling(); 195 self = self->parentElement(); 196 if (!self) { 197 ASSERT(!child); // We can only reach this if we are searching beyond the root element 198 return 0; // which cannot have siblings 199 } 200 } 201 } 202 203 static bool planCounter(RenderObject* object, const AtomicString& identifier, bool& isReset, int& value) 204 { 205 ASSERT(object); 206 207 // Real text nodes don't have their own style so they can't have counters. 208 // We can't even look at their styles or we'll see extra resets and increments! 209 if (object->isText() && !object->isBR()) 210 return false; 211 Node* generatingNode = object->generatingNode(); 212 // We must have a generating node or else we cannot have a counter. 213 if (!generatingNode) 214 return false; 215 RenderStyle* style = object->style(); 216 ASSERT(style); 217 218 switch (style->styleType()) { 219 case NOPSEUDO: 220 // Sometimes nodes have more then one renderer. Only the first one gets the counter 221 // LayoutTests/http/tests/css/counter-crash.html 222 if (generatingNode->renderer() != object) 223 return false; 224 break; 225 case BEFORE: 226 case AFTER: 227 break; 228 default: 229 return false; // Counters are forbidden from all other pseudo elements. 230 } 231 232 if (const CounterDirectiveMap* directivesMap = style->counterDirectives()) { 233 CounterDirectives directives = directivesMap->get(identifier.impl()); 234 if (directives.m_reset) { 235 value = directives.m_resetValue; 236 if (directives.m_increment) 237 value += directives.m_incrementValue; 238 isReset = true; 239 return true; 240 } 241 if (directives.m_increment) { 242 value = directives.m_incrementValue; 243 isReset = false; 244 return true; 245 } 246 } 247 248 if (identifier == "list-item") { 249 if (object->isListItem()) { 250 if (toRenderListItem(object)->hasExplicitValue()) { 251 value = toRenderListItem(object)->explicitValue(); 252 isReset = true; 253 return true; 254 } 255 value = 1; 256 isReset = false; 257 return true; 258 } 259 if (Node* e = object->node()) { 260 if (e->hasTagName(olTag)) { 261 value = static_cast<HTMLOListElement*>(e)->start(); 262 isReset = true; 263 return true; 264 } 265 if (e->hasTagName(ulTag) || e->hasTagName(menuTag) || e->hasTagName(dirTag)) { 266 value = 0; 267 isReset = true; 268 return true; 269 } 270 } 271 } 272 273 return false; 274 } 275 276 // - Finds the insertion point for the counter described by counterOwner, isReset and 277 // identifier in the CounterNode tree for identifier and sets parent and 278 // previousSibling accordingly. 279 // - The function returns true if the counter whose insertion point is searched is NOT 280 // the root of the tree. 281 // - The root of the tree is a counter reference that is not in the scope of any other 282 // counter with the same identifier. 283 // - All the counter references with the same identifier as this one that are in 284 // children or subsequent siblings of the renderer that owns the root of the tree 285 // form the rest of of the nodes of the tree. 286 // - The root of the tree is always a reset type reference. 287 // - A subtree rooted at any reset node in the tree is equivalent to all counter 288 // references that are in the scope of the counter or nested counter defined by that 289 // reset node. 290 // - Non-reset CounterNodes cannot have descendants. 291 292 static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& identifier, bool isReset, CounterNode*& parent, CounterNode*& previousSibling) 293 { 294 // We cannot stop searching for counters with the same identifier before we also 295 // check this renderer, because it may affect the positioning in the tree of our counter. 296 RenderObject* searchEndRenderer = previousSiblingOrParent(counterOwner); 297 // We check renderers in preOrder from the renderer that our counter is attached to 298 // towards the begining of the document for counters with the same identifier as the one 299 // we are trying to find a place for. This is the next renderer to be checked. 300 RenderObject* currentRenderer = previousInPreOrder(counterOwner); 301 previousSibling = 0; 302 while (currentRenderer) { 303 CounterNode* currentCounter = makeCounterNode(currentRenderer, identifier, false); 304 if (searchEndRenderer == currentRenderer) { 305 // We may be at the end of our search. 306 if (currentCounter) { 307 // We have a suitable counter on the EndSearchRenderer. 308 if (previousSibling) { // But we already found another counter that we come after. 309 if (currentCounter->actsAsReset()) { 310 // We found a reset counter that is on a renderer that is a sibling of ours or a parent. 311 if (isReset && areRenderersElementsSiblings(currentRenderer, counterOwner)) { 312 // We are also a reset counter and the previous reset was on a sibling renderer 313 // hence we are the next sibling of that counter if that reset is not a root or 314 // we are a root node if that reset is a root. 315 parent = currentCounter->parent(); 316 previousSibling = parent ? currentCounter : 0; 317 return parent; 318 } 319 // We are not a reset node or the previous reset must be on an ancestor of our owner renderer 320 // hence we must be a child of that reset counter. 321 parent = currentCounter; 322 ASSERT(previousSibling->parent() == currentCounter); 323 return true; 324 } 325 // CurrentCounter, the counter at the EndSearchRenderer, is not reset. 326 if (!isReset || !areRenderersElementsSiblings(currentRenderer, counterOwner)) { 327 // If the node we are placing is not reset or we have found a counter that is attached 328 // to an ancestor of the placed counter's owner renderer we know we are a sibling of that node. 329 ASSERT(currentCounter->parent() == previousSibling->parent()); 330 parent = currentCounter->parent(); 331 return true; 332 } 333 } else { 334 // We are at the potential end of the search, but we had no previous sibling candidate 335 // In this case we follow pretty much the same logic as above but no ASSERTs about 336 // previousSibling, and when we are a sibling of the end counter we must set previousSibling 337 // to currentCounter. 338 if (currentCounter->actsAsReset()) { 339 if (isReset && areRenderersElementsSiblings(currentRenderer, counterOwner)) { 340 parent = currentCounter->parent(); 341 previousSibling = currentCounter; 342 return parent; 343 } 344 parent = currentCounter; 345 return true; 346 } 347 if (!isReset || !areRenderersElementsSiblings(currentRenderer, counterOwner)) { 348 parent = currentCounter->parent(); 349 previousSibling = currentCounter; 350 return true; 351 } 352 previousSibling = currentCounter; 353 } 354 } 355 // We come here if the previous sibling or parent of our owner renderer had no 356 // good counter, or we are a reset node and the counter on the previous sibling 357 // of our owner renderer was not a reset counter. 358 // Set a new goal for the end of the search. 359 searchEndRenderer = previousSiblingOrParent(currentRenderer); 360 } else { 361 // We are searching descendants of a previous sibling of the renderer that the 362 // counter being placed is attached to. 363 if (currentCounter) { 364 // We found a suitable counter. 365 if (previousSibling) { 366 // Since we had a suitable previous counter before, we should only consider this one as our 367 // previousSibling if it is a reset counter and hence the current previousSibling is its child. 368 if (currentCounter->actsAsReset()) { 369 previousSibling = currentCounter; 370 // We are no longer interested in previous siblings of the currentRenderer or their children 371 // as counters they may have attached cannot be the previous sibling of the counter we are placing. 372 currentRenderer = parentElement(currentRenderer)->renderer(); 373 continue; 374 } 375 } else 376 previousSibling = currentCounter; 377 currentRenderer = previousSiblingOrParent(currentRenderer); 378 continue; 379 } 380 } 381 // This function is designed so that the same test is not done twice in an iteration, except for this one 382 // which may be done twice in some cases. Rearranging the decision points though, to accommodate this 383 // performance improvement would create more code duplication than is worthwhile in my oppinion and may further 384 // impede the readability of this already complex algorithm. 385 if (previousSibling) 386 currentRenderer = previousSiblingOrParent(currentRenderer); 387 else 388 currentRenderer = previousInPreOrder(currentRenderer); 389 } 390 return false; 391 } 392 393 static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& identifier, bool alwaysCreateCounter) 394 { 395 ASSERT(object); 396 397 if (object->m_hasCounterNodeMap) { 398 if (CounterMap* nodeMap = counterMaps().get(object)) { 399 if (CounterNode* node = nodeMap->get(identifier.impl()).get()) 400 return node; 401 } 402 } 403 404 bool isReset = false; 405 int value = 0; 406 if (!planCounter(object, identifier, isReset, value) && !alwaysCreateCounter) 407 return 0; 408 409 CounterNode* newParent = 0; 410 CounterNode* newPreviousSibling = 0; 411 RefPtr<CounterNode> newNode = CounterNode::create(object, isReset, value); 412 if (findPlaceForCounter(object, identifier, isReset, newParent, newPreviousSibling)) 413 newParent->insertAfter(newNode.get(), newPreviousSibling, identifier); 414 CounterMap* nodeMap; 415 if (object->m_hasCounterNodeMap) 416 nodeMap = counterMaps().get(object); 417 else { 418 nodeMap = new CounterMap; 419 counterMaps().set(object, nodeMap); 420 object->m_hasCounterNodeMap = true; 421 } 422 nodeMap->set(identifier.impl(), newNode); 423 if (newNode->parent()) 424 return newNode.get(); 425 // Checking if some nodes that were previously counter tree root nodes 426 // should become children of this node now. 427 CounterMaps& maps = counterMaps(); 428 Element* stayWithin = parentElement(object); 429 bool skipDescendants; 430 for (RenderObject* currentRenderer = nextInPreOrder(object, stayWithin); currentRenderer; currentRenderer = nextInPreOrder(currentRenderer, stayWithin, skipDescendants)) { 431 skipDescendants = false; 432 if (!currentRenderer->m_hasCounterNodeMap) 433 continue; 434 CounterNode* currentCounter = maps.get(currentRenderer)->get(identifier.impl()).get(); 435 if (!currentCounter) 436 continue; 437 skipDescendants = true; 438 if (currentCounter->parent()) { 439 ASSERT(newNode->firstChild()); 440 continue; 441 } 442 if (stayWithin == parentElement(currentRenderer) && currentCounter->hasResetType()) 443 break; 444 newNode->insertAfter(currentCounter, newNode->lastChild(), identifier); 445 } 446 return newNode.get(); 447 } 448 449 RenderCounter::RenderCounter(Document* node, const CounterContent& counter) 450 : RenderText(node, StringImpl::empty()) 451 , m_counter(counter) 452 , m_counterNode(0) 453 , m_nextForSameCounter(0) 454 { 455 } 456 457 RenderCounter::~RenderCounter() 458 { 459 if (m_counterNode) { 460 m_counterNode->removeRenderer(this); 461 ASSERT(!m_counterNode); 462 } 463 } 464 465 const char* RenderCounter::renderName() const 466 { 467 return "RenderCounter"; 468 } 469 470 bool RenderCounter::isCounter() const 471 { 472 return true; 473 } 474 475 PassRefPtr<StringImpl> RenderCounter::originalText() const 476 { 477 if (!m_counterNode) { 478 RenderObject* beforeAfterContainer = parent(); 479 while (true) { 480 if (!beforeAfterContainer) 481 return 0; 482 if (!beforeAfterContainer->isAnonymous()) 483 return 0; // RenderCounters are restricted to before and after pseudo elements 484 PseudoId containerStyle = beforeAfterContainer->style()->styleType(); 485 if ((containerStyle == BEFORE) || (containerStyle == AFTER)) 486 break; 487 beforeAfterContainer = beforeAfterContainer->parent(); 488 } 489 makeCounterNode(beforeAfterContainer, m_counter.identifier(), true)->addRenderer(const_cast<RenderCounter*>(this)); 490 ASSERT(m_counterNode); 491 } 492 CounterNode* child = m_counterNode; 493 int value = child->actsAsReset() ? child->value() : child->countInParent(); 494 495 String text = listMarkerText(m_counter.listStyle(), value); 496 497 if (!m_counter.separator().isNull()) { 498 if (!child->actsAsReset()) 499 child = child->parent(); 500 while (CounterNode* parent = child->parent()) { 501 text = listMarkerText(m_counter.listStyle(), child->countInParent()) 502 + m_counter.separator() + text; 503 child = parent; 504 } 505 } 506 507 return text.impl(); 508 } 509 510 void RenderCounter::computePreferredLogicalWidths(float lead) 511 { 512 setTextInternal(originalText()); 513 RenderText::computePreferredLogicalWidths(lead); 514 } 515 516 void RenderCounter::invalidate() 517 { 518 m_counterNode->removeRenderer(this); 519 ASSERT(!m_counterNode); 520 if (documentBeingDestroyed()) 521 return; 522 setNeedsLayoutAndPrefWidthsRecalc(); 523 } 524 525 static void destroyCounterNodeWithoutMapRemoval(const AtomicString& identifier, CounterNode* node) 526 { 527 CounterNode* previous; 528 for (RefPtr<CounterNode> child = node->lastDescendant(); child && child != node; child = previous) { 529 previous = child->previousInPreOrder(); 530 child->parent()->removeChild(child.get()); 531 ASSERT(counterMaps().get(child->owner())->get(identifier.impl()) == child); 532 counterMaps().get(child->owner())->remove(identifier.impl()); 533 } 534 if (CounterNode* parent = node->parent()) 535 parent->removeChild(node); 536 } 537 538 void RenderCounter::destroyCounterNodes(RenderObject* owner) 539 { 540 CounterMaps& maps = counterMaps(); 541 CounterMaps::iterator mapsIterator = maps.find(owner); 542 if (mapsIterator == maps.end()) 543 return; 544 CounterMap* map = mapsIterator->second; 545 CounterMap::const_iterator end = map->end(); 546 for (CounterMap::const_iterator it = map->begin(); it != end; ++it) { 547 AtomicString identifier(it->first.get()); 548 destroyCounterNodeWithoutMapRemoval(identifier, it->second.get()); 549 } 550 maps.remove(mapsIterator); 551 delete map; 552 owner->m_hasCounterNodeMap = false; 553 } 554 555 void RenderCounter::destroyCounterNode(RenderObject* owner, const AtomicString& identifier) 556 { 557 CounterMap* map = counterMaps().get(owner); 558 if (!map) 559 return; 560 CounterMap::iterator mapIterator = map->find(identifier.impl()); 561 if (mapIterator == map->end()) 562 return; 563 destroyCounterNodeWithoutMapRemoval(identifier, mapIterator->second.get()); 564 map->remove(mapIterator); 565 // We do not delete "map" here even if empty because we expect to reuse 566 // it soon. In order for a renderer to lose all its counters permanently, 567 // a style change for the renderer involving removal of all counter 568 // directives must occur, in which case, RenderCounter::destroyCounterNodes() 569 // must be called. 570 // The destruction of the Renderer (possibly caused by the removal of its 571 // associated DOM node) is the other case that leads to the permanent 572 // destruction of all counters attached to a Renderer. In this case 573 // RenderCounter::destroyCounterNodes() must be and is now called, too. 574 // RenderCounter::destroyCounterNodes() handles destruction of the counter 575 // map associated with a renderer, so there is no risk in leaking the map. 576 } 577 578 void RenderCounter::rendererRemovedFromTree(RenderObject* removedRenderer) 579 { 580 RenderObject* currentRenderer = removedRenderer->lastLeafChild(); 581 if (!currentRenderer) 582 currentRenderer = removedRenderer; 583 while (true) { 584 destroyCounterNodes(currentRenderer); 585 if (currentRenderer == removedRenderer) 586 break; 587 currentRenderer = currentRenderer->previousInPreOrder(); 588 } 589 } 590 591 static void updateCounters(RenderObject* renderer) 592 { 593 ASSERT(renderer->style()); 594 const CounterDirectiveMap* directiveMap = renderer->style()->counterDirectives(); 595 if (!directiveMap) 596 return; 597 CounterDirectiveMap::const_iterator end = directiveMap->end(); 598 if (!renderer->m_hasCounterNodeMap) { 599 for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) 600 makeCounterNode(renderer, AtomicString(it->first.get()), false); 601 return; 602 } 603 CounterMap* counterMap = counterMaps().get(renderer); 604 ASSERT(counterMap); 605 for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) { 606 RefPtr<CounterNode> node = counterMap->get(it->first.get()); 607 if (!node) { 608 makeCounterNode(renderer, AtomicString(it->first.get()), false); 609 continue; 610 } 611 CounterNode* newParent = 0; 612 CounterNode* newPreviousSibling; 613 614 findPlaceForCounter(renderer, AtomicString(it->first.get()), node->hasResetType(), newParent, newPreviousSibling); 615 if (node != counterMap->get(it->first.get())) 616 continue; 617 CounterNode* parent = node->parent(); 618 if (newParent == parent && newPreviousSibling == node->previousSibling()) 619 continue; 620 if (parent) 621 parent->removeChild(node.get()); 622 if (newParent) 623 newParent->insertAfter(node.get(), newPreviousSibling, it->first.get()); 624 } 625 } 626 627 void RenderCounter::rendererSubtreeAttached(RenderObject* renderer) 628 { 629 Node* node = renderer->node(); 630 if (node) 631 node = node->parentNode(); 632 else 633 node = renderer->generatingNode(); 634 if (node && !node->attached()) 635 return; // No need to update if the parent is not attached yet 636 for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer)) 637 updateCounters(descendant); 638 } 639 640 void RenderCounter::rendererStyleChanged(RenderObject* renderer, const RenderStyle* oldStyle, const RenderStyle* newStyle) 641 { 642 Node* node = renderer->generatingNode(); 643 if (!node || !node->attached()) 644 return; // cannot have generated content or if it can have, it will be handled during attaching 645 const CounterDirectiveMap* newCounterDirectives; 646 const CounterDirectiveMap* oldCounterDirectives; 647 if (oldStyle && (oldCounterDirectives = oldStyle->counterDirectives())) { 648 if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) { 649 CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); 650 CounterDirectiveMap::const_iterator oldMapEnd = oldCounterDirectives->end(); 651 for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { 652 CounterDirectiveMap::const_iterator oldMapIt = oldCounterDirectives->find(it->first); 653 if (oldMapIt != oldMapEnd) { 654 if (oldMapIt->second == it->second) 655 continue; 656 RenderCounter::destroyCounterNode(renderer, it->first.get()); 657 } 658 // We must create this node here, because the changed node may be a node with no display such as 659 // as those created by the increment or reset directives and the re-layout that will happen will 660 // not catch the change if the node had no children. 661 makeCounterNode(renderer, it->first.get(), false); 662 } 663 // Destroying old counters that do not exist in the new counterDirective map. 664 for (CounterDirectiveMap::const_iterator it = oldCounterDirectives->begin(); it !=oldMapEnd; ++it) { 665 if (!newCounterDirectives->contains(it->first)) 666 RenderCounter::destroyCounterNode(renderer, it->first.get()); 667 } 668 } else { 669 if (renderer->m_hasCounterNodeMap) 670 RenderCounter::destroyCounterNodes(renderer); 671 } 672 } else if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) { 673 CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); 674 for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { 675 // We must create this node here, because the added node may be a node with no display such as 676 // as those created by the increment or reset directives and the re-layout that will happen will 677 // not catch the change if the node had no children. 678 makeCounterNode(renderer, it->first.get(), false); 679 } 680 } 681 } 682 683 } // namespace WebCore 684 685 #ifndef NDEBUG 686 687 void showCounterRendererTree(const WebCore::RenderObject* renderer, const char* counterName) 688 { 689 if (!renderer) 690 return; 691 const WebCore::RenderObject* root = renderer; 692 while (root->parent()) 693 root = root->parent(); 694 695 AtomicString identifier(counterName); 696 for (const WebCore::RenderObject* current = root; current; current = current->nextInPreOrder()) { 697 fprintf(stderr, "%c", (current == renderer) ? '*' : ' '); 698 for (const WebCore::RenderObject* parent = current; parent && parent != root; parent = parent->parent()) 699 fprintf(stderr, " "); 700 fprintf(stderr, "%p N:%p P:%p PS:%p NS:%p C:%p\n", 701 current, current->node(), current->parent(), current->previousSibling(), 702 current->nextSibling(), current->m_hasCounterNodeMap? 703 counterName ? WebCore::counterMaps().get(current)->get(identifier.impl()).get() : (WebCore::CounterNode*)1 : (WebCore::CounterNode*)0); 704 } 705 fflush(stderr); 706 } 707 708 #endif // NDEBUG 709