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 "HTMLNames.h" 28 #include "HTMLOListElement.h" 29 #include "RenderListItem.h" 30 #include "RenderListMarker.h" 31 #include "RenderStyle.h" 32 #include <wtf/StdLibExtras.h> 33 34 namespace WebCore { 35 36 using namespace HTMLNames; 37 38 typedef HashMap<RefPtr<AtomicStringImpl>, CounterNode*> CounterMap; 39 typedef HashMap<const RenderObject*, CounterMap*> CounterMaps; 40 41 static CounterNode* makeCounterNode(RenderObject*, const AtomicString& identifier, bool alwaysCreateCounter); 42 43 static CounterMaps& counterMaps() 44 { 45 DEFINE_STATIC_LOCAL(CounterMaps, staticCounterMaps, ()); 46 return staticCounterMaps; 47 } 48 49 static inline RenderObject* previousSiblingOrParent(RenderObject* object) 50 { 51 if (RenderObject* sibling = object->previousSibling()) 52 return sibling; 53 return object->parent(); 54 } 55 56 static bool planCounter(RenderObject* object, const AtomicString& identifier, bool& isReset, int& value) 57 { 58 ASSERT(object); 59 60 // Real text nodes don't have their own style so they can't have counters. 61 // We can't even look at their styles or we'll see extra resets and increments! 62 if (object->isText() && !object->isBR()) 63 return false; 64 65 RenderStyle* style = object->style(); 66 ASSERT(style); 67 68 if (const CounterDirectiveMap* directivesMap = style->counterDirectives()) { 69 CounterDirectives directives = directivesMap->get(identifier.impl()); 70 if (directives.m_reset) { 71 value = directives.m_resetValue; 72 if (directives.m_increment) 73 value += directives.m_incrementValue; 74 isReset = true; 75 return true; 76 } 77 if (directives.m_increment) { 78 value = directives.m_incrementValue; 79 isReset = false; 80 return true; 81 } 82 } 83 84 if (identifier == "list-item") { 85 if (object->isListItem()) { 86 if (toRenderListItem(object)->hasExplicitValue()) { 87 value = toRenderListItem(object)->explicitValue(); 88 isReset = true; 89 return true; 90 } 91 value = 1; 92 isReset = false; 93 return true; 94 } 95 if (Node* e = object->node()) { 96 if (e->hasTagName(olTag)) { 97 value = static_cast<HTMLOListElement*>(e)->start(); 98 isReset = true; 99 return true; 100 } 101 if (e->hasTagName(ulTag) || e->hasTagName(menuTag) || e->hasTagName(dirTag)) { 102 value = 0; 103 isReset = true; 104 return true; 105 } 106 } 107 } 108 109 return false; 110 } 111 112 // - Finds the insertion point for the counter described by counterOwner, isReset and 113 // identifier in the CounterNode tree for identifier and sets parent and 114 // previousSibling accordingly. 115 // - The function returns true if the counter whose insertion point is searched is NOT 116 // the root of the tree. 117 // - The root of the tree is a counter reference that is not in the scope of any other 118 // counter with the same identifier. 119 // - All the counter references with the same identifier as this one that are in 120 // children or subsequent siblings of the renderer that owns the root of the tree 121 // form the rest of of the nodes of the tree. 122 // - The root of the tree is always a reset type reference. 123 // - A subtree rooted at any reset node in the tree is equivalent to all counter 124 // references that are in the scope of the counter or nested counter defined by that 125 // reset node. 126 // - Non-reset CounterNodes cannot have descendants. 127 128 static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& identifier, bool isReset, CounterNode*& parent, CounterNode*& previousSibling) 129 { 130 // We cannot stop searching for counters with the same identifier before we also 131 // check this renderer, because it may affect the positioning in the tree of our counter. 132 RenderObject* searchEndRenderer = previousSiblingOrParent(counterOwner); 133 // We check renderers in preOrder from the renderer that our counter is attached to 134 // towards the begining of the document for counters with the same identifier as the one 135 // we are trying to find a place for. This is the next renderer to be checked. 136 RenderObject* currentRenderer = counterOwner->previousInPreOrder(); 137 previousSibling = 0; 138 while (currentRenderer) { 139 // A sibling without a parent means that the counter node tree was not constructed correctly so we stop 140 // traversing. In the future RenderCounter should handle RenderObjects that are not connected to the 141 // render tree at counter node creation. See bug 43812. 142 if (previousSibling && !previousSibling->parent()) 143 return false; 144 CounterNode* currentCounter = makeCounterNode(currentRenderer, identifier, false); 145 if (searchEndRenderer == currentRenderer) { 146 // We may be at the end of our search. 147 if (currentCounter) { 148 // We have a suitable counter on the EndSearchRenderer. 149 if (previousSibling) { // But we already found another counter that we come after. 150 if (currentCounter->actsAsReset()) { 151 // We found a reset counter that is on a renderer that is a sibling of ours or a parent. 152 if (isReset && currentRenderer->parent() == counterOwner->parent()) { 153 // We are also a reset counter and the previous reset was on a sibling renderer 154 // hence we are the next sibling of that counter if that reset is not a root or 155 // we are a root node if that reset is a root. 156 parent = currentCounter->parent(); 157 previousSibling = parent ? currentCounter : 0; 158 return parent; 159 } 160 // We are not a reset node or the previous reset must be on an ancestor of our renderer 161 // hence we must be a child of that reset counter. 162 parent = currentCounter; 163 ASSERT(previousSibling->parent() == currentCounter); 164 return true; 165 } 166 // CurrentCounter, the counter at the EndSearchRenderer, is not reset. 167 if (!isReset || currentRenderer->parent() != counterOwner->parent()) { 168 // If the node we are placing is not reset or we have found a counter that is attached 169 // to an ancestor of the placed counter's renderer we know we are a sibling of that node. 170 ASSERT(currentCounter->parent() == previousSibling->parent()); 171 parent = currentCounter->parent(); 172 return true; 173 } 174 } else { 175 // We are at the potential end of the search, but we had no previous sibling candidate 176 // In this case we follow pretty much the same logic as above but no ASSERTs about 177 // previousSibling, and when we are a sibling of the end counter we must set previousSibling 178 // to currentCounter. 179 if (currentCounter->actsAsReset()) { 180 if (isReset && currentRenderer->parent() == counterOwner->parent()) { 181 parent = currentCounter->parent(); 182 previousSibling = currentCounter; 183 return parent; 184 } 185 parent = currentCounter; 186 return true; 187 } 188 if (!isReset || currentRenderer->parent() != counterOwner->parent()) { 189 parent = currentCounter->parent(); 190 previousSibling = currentCounter; 191 return true; 192 } 193 previousSibling = currentCounter; 194 } 195 } 196 // We come here if the previous sibling or parent of our renderer had no 197 // good counter, or we are a reset node and the counter on the previous sibling 198 // of our renderer was not a reset counter. 199 // Set a new goal for the end of the search. 200 searchEndRenderer = previousSiblingOrParent(currentRenderer); 201 } else { 202 // We are searching descendants of a previous sibling of the renderer that the 203 // counter being placed is attached to. 204 if (currentCounter) { 205 // We found a suitable counter. 206 if (previousSibling) { 207 // Since we had a suitable previous counter before, we should only consider this one as our 208 // previousSibling if it is a reset counter and hence the current previousSibling is its child. 209 if (currentCounter->actsAsReset()) { 210 previousSibling = currentCounter; 211 // We are no longer interested in previous siblings of the currentRenderer or their children 212 // as counters they may have attached cannot be the previous sibling of the counter we are placing. 213 currentRenderer = currentRenderer->parent(); 214 continue; 215 } 216 } else 217 previousSibling = currentCounter; 218 currentRenderer = previousSiblingOrParent(currentRenderer); 219 continue; 220 } 221 } 222 // This function is designed so that the same test is not done twice in an iteration, except for this one 223 // which may be done twice in some cases. Rearranging the decision points though, to accommodate this 224 // performance improvement would create more code duplication than is worthwhile in my oppinion and may further 225 // impede the readability of this already complex algorithm. 226 if (previousSibling) 227 currentRenderer = previousSiblingOrParent(currentRenderer); 228 else 229 currentRenderer = currentRenderer->previousInPreOrder(); 230 } 231 return false; 232 } 233 234 static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& identifier, bool alwaysCreateCounter) 235 { 236 ASSERT(object); 237 238 if (object->m_hasCounterNodeMap) 239 if (CounterMap* nodeMap = counterMaps().get(object)) 240 if (CounterNode* node = nodeMap->get(identifier.impl())) 241 return node; 242 243 bool isReset = false; 244 int value = 0; 245 if (!planCounter(object, identifier, isReset, value) && !alwaysCreateCounter) 246 return 0; 247 248 CounterNode* newParent = 0; 249 CounterNode* newPreviousSibling = 0; 250 CounterNode* newNode = new CounterNode(object, isReset, value); 251 if (findPlaceForCounter(object, identifier, isReset, newParent, newPreviousSibling)) 252 newParent->insertAfter(newNode, newPreviousSibling, identifier); 253 CounterMap* nodeMap; 254 if (object->m_hasCounterNodeMap) 255 nodeMap = counterMaps().get(object); 256 else { 257 nodeMap = new CounterMap; 258 counterMaps().set(object, nodeMap); 259 object->m_hasCounterNodeMap = true; 260 } 261 nodeMap->set(identifier.impl(), newNode); 262 if (newNode->parent() || !object->nextInPreOrder(object->parent())) 263 return newNode; 264 // Checking if some nodes that were previously counter tree root nodes 265 // should become children of this node now. 266 CounterMaps& maps = counterMaps(); 267 RenderObject* stayWithin = object->parent(); 268 for (RenderObject* currentRenderer = object->nextInPreOrder(stayWithin); currentRenderer; currentRenderer = currentRenderer->nextInPreOrder(stayWithin)) { 269 if (!currentRenderer->m_hasCounterNodeMap) 270 continue; 271 CounterNode* currentCounter = maps.get(currentRenderer)->get(identifier.impl()); 272 if (!currentCounter) 273 continue; 274 if (currentCounter->parent()) { 275 ASSERT(newNode->firstChild()); 276 if (currentRenderer->lastChild()) 277 currentRenderer = currentRenderer->lastChild(); 278 continue; 279 } 280 if (stayWithin != currentRenderer->parent() || !currentCounter->hasResetType()) 281 newNode->insertAfter(currentCounter, newNode->lastChild(), identifier); 282 if (currentRenderer->lastChild()) 283 currentRenderer = currentRenderer->lastChild(); 284 } 285 return newNode; 286 } 287 288 RenderCounter::RenderCounter(Document* node, const CounterContent& counter) 289 : RenderText(node, StringImpl::empty()) 290 , m_counter(counter) 291 , m_counterNode(0) 292 { 293 } 294 295 const char* RenderCounter::renderName() const 296 { 297 return "RenderCounter"; 298 } 299 300 bool RenderCounter::isCounter() const 301 { 302 return true; 303 } 304 305 PassRefPtr<StringImpl> RenderCounter::originalText() const 306 { 307 if (!parent()) 308 return 0; 309 310 if (!m_counterNode) 311 m_counterNode = makeCounterNode(parent(), m_counter.identifier(), true); 312 313 CounterNode* child = m_counterNode; 314 int value = child->actsAsReset() ? child->value() : child->countInParent(); 315 316 String text = listMarkerText(m_counter.listStyle(), value); 317 318 if (!m_counter.separator().isNull()) { 319 if (!child->actsAsReset()) 320 child = child->parent(); 321 while (CounterNode* parent = child->parent()) { 322 text = listMarkerText(m_counter.listStyle(), child->countInParent()) 323 + m_counter.separator() + text; 324 child = parent; 325 } 326 } 327 328 return text.impl(); 329 } 330 331 void RenderCounter::calcPrefWidths(int lead) 332 { 333 setTextInternal(originalText()); 334 RenderText::calcPrefWidths(lead); 335 } 336 337 void RenderCounter::invalidate(const AtomicString& identifier) 338 { 339 if (m_counter.identifier() != identifier) 340 return; 341 m_counterNode = 0; 342 setNeedsLayoutAndPrefWidthsRecalc(); 343 } 344 345 static void destroyCounterNodeWithoutMapRemoval(const AtomicString& identifier, CounterNode* node) 346 { 347 CounterNode* previous; 348 for (CounterNode* child = node->lastDescendant(); child && child != node; child = previous) { 349 previous = child->previousInPreOrder(); 350 child->parent()->removeChild(child, identifier); 351 ASSERT(counterMaps().get(child->renderer())->get(identifier.impl()) == child); 352 counterMaps().get(child->renderer())->remove(identifier.impl()); 353 if (!child->renderer()->documentBeingDestroyed()) { 354 RenderObjectChildList* children = child->renderer()->virtualChildren(); 355 if (children) 356 children->invalidateCounters(child->renderer(), identifier); 357 } 358 delete child; 359 } 360 RenderObject* renderer = node->renderer(); 361 if (!renderer->documentBeingDestroyed()) { 362 if (RenderObjectChildList* children = renderer->virtualChildren()) 363 children->invalidateCounters(renderer, identifier); 364 } 365 if (CounterNode* parent = node->parent()) 366 parent->removeChild(node, identifier); 367 delete node; 368 } 369 370 void RenderCounter::destroyCounterNodes(RenderObject* renderer) 371 { 372 CounterMaps& maps = counterMaps(); 373 CounterMaps::iterator mapsIterator = maps.find(renderer); 374 if (mapsIterator == maps.end()) 375 return; 376 CounterMap* map = mapsIterator->second; 377 CounterMap::const_iterator end = map->end(); 378 for (CounterMap::const_iterator it = map->begin(); it != end; ++it) { 379 AtomicString identifier(it->first.get()); 380 destroyCounterNodeWithoutMapRemoval(identifier, it->second); 381 } 382 maps.remove(mapsIterator); 383 delete map; 384 renderer->m_hasCounterNodeMap = false; 385 } 386 387 void RenderCounter::destroyCounterNode(RenderObject* renderer, const AtomicString& identifier) 388 { 389 CounterMap* map = counterMaps().get(renderer); 390 if (!map) 391 return; 392 CounterMap::iterator mapIterator = map->find(identifier.impl()); 393 if (mapIterator == map->end()) 394 return; 395 destroyCounterNodeWithoutMapRemoval(identifier, mapIterator->second); 396 map->remove(mapIterator); 397 // We do not delete "map" here even if empty because we expect to reuse 398 // it soon. In order for a renderer to lose all its counters permanently, 399 // a style change for the renderer involving removal of all counter 400 // directives must occur, in which case, RenderCounter::destroyCounterNodes() 401 // must be called. 402 // The destruction of the Renderer (possibly caused by the removal of its 403 // associated DOM node) is the other case that leads to the permanent 404 // destruction of all counters attached to a Renderer. In this case 405 // RenderCounter::destroyCounterNodes() must be and is now called, too. 406 // RenderCounter::destroyCounterNodes() handles destruction of the counter 407 // map associated with a renderer, so there is no risk in leaking the map. 408 } 409 410 static void updateCounters(RenderObject* renderer) 411 { 412 ASSERT(renderer->style()); 413 const CounterDirectiveMap* directiveMap = renderer->style()->counterDirectives(); 414 if (!directiveMap) 415 return; 416 CounterDirectiveMap::const_iterator end = directiveMap->end(); 417 if (!renderer->m_hasCounterNodeMap) { 418 for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) 419 makeCounterNode(renderer, AtomicString(it->first.get()), false); 420 return; 421 } 422 CounterMap* counterMap = counterMaps().get(renderer); 423 ASSERT(counterMap); 424 for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) { 425 CounterNode* node = counterMap->get(it->first.get()); 426 if (!node) { 427 makeCounterNode(renderer, AtomicString(it->first.get()), false); 428 continue; 429 } 430 CounterNode* newParent = 0; 431 CounterNode* newPreviousSibling; 432 findPlaceForCounter(renderer, AtomicString(it->first.get()), node->hasResetType(), newParent, newPreviousSibling); 433 CounterNode* parent = node->parent(); 434 if (newParent == parent && newPreviousSibling == node->previousSibling()) 435 continue; 436 if (parent) 437 parent->removeChild(node, it->first.get()); 438 if (newParent) 439 newParent->insertAfter(node, newPreviousSibling, it->first.get()); 440 } 441 } 442 443 void RenderCounter::rendererSubtreeAttached(RenderObject* renderer) 444 { 445 for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer)) 446 updateCounters(descendant); 447 } 448 449 void RenderCounter::rendererStyleChanged(RenderObject* renderer, const RenderStyle* oldStyle, const RenderStyle* newStyle) 450 { 451 const CounterDirectiveMap* newCounterDirectives; 452 const CounterDirectiveMap* oldCounterDirectives; 453 if (oldStyle && (oldCounterDirectives = oldStyle->counterDirectives())) { 454 if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) { 455 CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); 456 CounterDirectiveMap::const_iterator oldMapEnd = oldCounterDirectives->end(); 457 for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { 458 CounterDirectiveMap::const_iterator oldMapIt = oldCounterDirectives->find(it->first); 459 if (oldMapIt != oldMapEnd) { 460 if (oldMapIt->second == it->second) 461 continue; 462 RenderCounter::destroyCounterNode(renderer, it->first.get()); 463 } 464 // We must create this node here, because the changed node may be a node with no display such as 465 // as those created by the increment or reset directives and the re-layout that will happen will 466 // not catch the change if the node had no children. 467 makeCounterNode(renderer, it->first.get(), false); 468 } 469 // Destroying old counters that do not exist in the new counterDirective map. 470 for (CounterDirectiveMap::const_iterator it = oldCounterDirectives->begin(); it !=oldMapEnd; ++it) { 471 if (!newCounterDirectives->contains(it->first)) 472 RenderCounter::destroyCounterNode(renderer, it->first.get()); 473 } 474 } else { 475 if (renderer->m_hasCounterNodeMap) 476 RenderCounter::destroyCounterNodes(renderer); 477 } 478 } else if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) { 479 CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); 480 for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { 481 // We must create this node here, because the added node may be a node with no display such as 482 // as those created by the increment or reset directives and the re-layout that will happen will 483 // not catch the change if the node had no children. 484 makeCounterNode(renderer, it->first.get(), false); 485 } 486 } 487 } 488 489 } // namespace WebCore 490