RenderCounter.cpp revision 5e2bc6953fe6923165b8a5d7679939693a1d58d6
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 "HTMLNames.h" 28#include "HTMLOListElement.h" 29#include "RenderListItem.h" 30#include "RenderListMarker.h" 31#include "RenderStyle.h" 32#include <wtf/StdLibExtras.h> 33 34namespace WebCore { 35 36using namespace HTMLNames; 37 38typedef HashMap<RefPtr<AtomicStringImpl>, CounterNode*> CounterMap; 39typedef HashMap<const RenderObject*, CounterMap*> CounterMaps; 40 41static CounterNode* makeCounterNode(RenderObject*, const AtomicString& identifier, bool alwaysCreateCounter); 42 43static CounterMaps& counterMaps() 44{ 45 DEFINE_STATIC_LOCAL(CounterMaps, staticCounterMaps, ()); 46 return staticCounterMaps; 47} 48 49static inline RenderObject* previousSiblingOrParent(RenderObject* object) 50{ 51 if (RenderObject* sibling = object->previousSibling()) 52 return sibling; 53 return object->parent(); 54} 55 56static 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 128static 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 CounterNode* currentCounter = makeCounterNode(currentRenderer, identifier, false); 140 if (searchEndRenderer == currentRenderer) { 141 // We may be at the end of our search. 142 if (currentCounter) { 143 // We have a suitable counter on the EndSearchRenderer. 144 if (previousSibling) { // But we already found another counter that we come after. 145 if (currentCounter->actsAsReset()) { 146 // We found a reset counter that is on a renderer that is a sibling of ours or a parent. 147 if (isReset && currentRenderer->parent() == counterOwner->parent()) { 148 // We are also a reset counter and the previous reset was on a sibling renderer 149 // hence we are the next sibling of that counter if that reset is not a root or 150 // we are a root node if that reset is a root. 151 parent = currentCounter->parent(); 152 previousSibling = parent ? currentCounter : 0; 153 return parent; 154 } 155 // We are not a reset node or the previous reset must be on an ancestor of our renderer 156 // hence we must be a child of that reset counter. 157 parent = currentCounter; 158 ASSERT(previousSibling->parent() == currentCounter); 159 return true; 160 } 161 // CurrentCounter, the counter at the EndSearchRenderer, is not reset. 162 if (!isReset || currentRenderer->parent() != counterOwner->parent()) { 163 // If the node we are placing is not reset or we have found a counter that is attached 164 // to an ancestor of the placed counter's renderer we know we are a sibling of that node. 165 ASSERT(currentCounter->parent() == previousSibling->parent()); 166 parent = currentCounter->parent(); 167 return true; 168 } 169 } else { 170 // We are at the potential end of the search, but we had no previous sibling candidate 171 // In this case we follow pretty much the same logic as above but no ASSERTs about 172 // previousSibling, and when we are a sibling of the end counter we must set previousSibling 173 // to currentCounter. 174 if (currentCounter->actsAsReset()) { 175 if (isReset && currentRenderer->parent() == counterOwner->parent()) { 176 parent = currentCounter->parent(); 177 previousSibling = currentCounter; 178 return parent; 179 } 180 parent = currentCounter; 181 return true; 182 } 183 if (!isReset || currentRenderer->parent() != counterOwner->parent()) { 184 parent = currentCounter->parent(); 185 previousSibling = currentCounter; 186 return true; 187 } 188 previousSibling = currentCounter; 189 } 190 } 191 // We come here if the previous sibling or parent of our renderer had no 192 // good counter, or we are a reset node and the counter on the previous sibling 193 // of our renderer was not a reset counter. 194 // Set a new goal for the end of the search. 195 searchEndRenderer = previousSiblingOrParent(currentRenderer); 196 } else { 197 // We are searching descendants of a previous sibling of the renderer that the 198 // counter being placed is attached to. 199 if (currentCounter) { 200 // We found a suitable counter. 201 if (previousSibling) { 202 // Since we had a suitable previous counter before, we should only consider this one as our 203 // previousSibling if it is a reset counter and hence the current previousSibling is its child. 204 if (currentCounter->actsAsReset()) { 205 previousSibling = currentCounter; 206 // We are no longer interested in previous siblings of the currentRenderer or their children 207 // as counters they may have attached cannot be the previous sibling of the counter we are placing. 208 currentRenderer = currentRenderer->parent(); 209 continue; 210 } 211 } else 212 previousSibling = currentCounter; 213 currentRenderer = previousSiblingOrParent(currentRenderer); 214 continue; 215 } 216 } 217 // This function is designed so that the same test is not done twice in an iteration, except for this one 218 // which may be done twice in some cases. Rearranging the decision points though, to accommodate this 219 // performance improvement would create more code duplication than is worthwhile in my oppinion and may further 220 // impede the readability of this already complex algorithm. 221 if (previousSibling) 222 currentRenderer = previousSiblingOrParent(currentRenderer); 223 else 224 currentRenderer = currentRenderer->previousInPreOrder(); 225 } 226 return false; 227} 228 229static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& identifier, bool alwaysCreateCounter) 230{ 231 ASSERT(object); 232 233 if (object->m_hasCounterNodeMap) 234 if (CounterMap* nodeMap = counterMaps().get(object)) 235 if (CounterNode* node = nodeMap->get(identifier.impl())) 236 return node; 237 238 bool isReset = false; 239 int value = 0; 240 if (!planCounter(object, identifier, isReset, value) && !alwaysCreateCounter) 241 return 0; 242 243 CounterNode* newParent = 0; 244 CounterNode* newPreviousSibling = 0; 245 CounterNode* newNode = new CounterNode(object, isReset, value); 246 if (findPlaceForCounter(object, identifier, isReset, newParent, newPreviousSibling)) 247 newParent->insertAfter(newNode, newPreviousSibling, identifier); 248 CounterMap* nodeMap; 249 if (object->m_hasCounterNodeMap) 250 nodeMap = counterMaps().get(object); 251 else { 252 nodeMap = new CounterMap; 253 counterMaps().set(object, nodeMap); 254 object->m_hasCounterNodeMap = true; 255 } 256 nodeMap->set(identifier.impl(), newNode); 257 if (newNode->parent() || !object->nextInPreOrder(object->parent())) 258 return newNode; 259 // Checking if some nodes that were previously counter tree root nodes 260 // should become children of this node now. 261 CounterMaps& maps = counterMaps(); 262 RenderObject* stayWithin = object->parent(); 263 for (RenderObject* currentRenderer = object->nextInPreOrder(stayWithin); currentRenderer; currentRenderer = currentRenderer->nextInPreOrder(stayWithin)) { 264 if (!currentRenderer->m_hasCounterNodeMap) 265 continue; 266 CounterNode* currentCounter = maps.get(currentRenderer)->get(identifier.impl()); 267 if (!currentCounter) 268 continue; 269 if (currentCounter->parent()) { 270 ASSERT(newNode->firstChild()); 271 if (currentRenderer->lastChild()) 272 currentRenderer = currentRenderer->lastChild(); 273 continue; 274 } 275 if (stayWithin != currentRenderer->parent() || !currentCounter->hasResetType()) 276 newNode->insertAfter(currentCounter, newNode->lastChild(), identifier); 277 if (currentRenderer->lastChild()) 278 currentRenderer = currentRenderer->lastChild(); 279 } 280 return newNode; 281} 282 283RenderCounter::RenderCounter(Document* node, const CounterContent& counter) 284 : RenderText(node, StringImpl::empty()) 285 , m_counter(counter) 286 , m_counterNode(0) 287{ 288} 289 290const char* RenderCounter::renderName() const 291{ 292 return "RenderCounter"; 293} 294 295bool RenderCounter::isCounter() const 296{ 297 return true; 298} 299 300PassRefPtr<StringImpl> RenderCounter::originalText() const 301{ 302 if (!parent()) 303 return 0; 304 305 if (!m_counterNode) 306 m_counterNode = makeCounterNode(parent(), m_counter.identifier(), true); 307 308 CounterNode* child = m_counterNode; 309 int value = child->actsAsReset() ? child->value() : child->countInParent(); 310 311 String text = listMarkerText(m_counter.listStyle(), value); 312 313 if (!m_counter.separator().isNull()) { 314 if (!child->actsAsReset()) 315 child = child->parent(); 316 while (CounterNode* parent = child->parent()) { 317 text = listMarkerText(m_counter.listStyle(), child->countInParent()) 318 + m_counter.separator() + text; 319 child = parent; 320 } 321 } 322 323 return text.impl(); 324} 325 326void RenderCounter::calcPrefWidths(int lead) 327{ 328 setTextInternal(originalText()); 329 RenderText::calcPrefWidths(lead); 330} 331 332void RenderCounter::invalidate(const AtomicString& identifier) 333{ 334 if (m_counter.identifier() != identifier) 335 return; 336 m_counterNode = 0; 337 setNeedsLayoutAndPrefWidthsRecalc(); 338} 339 340static void destroyCounterNodeWithoutMapRemoval(const AtomicString& identifier, CounterNode* node) 341{ 342 CounterNode* previous; 343 for (CounterNode* child = node->lastDescendant(); child && child != node; child = previous) { 344 previous = child->previousInPreOrder(); 345 child->parent()->removeChild(child, identifier); 346 ASSERT(counterMaps().get(child->renderer())->get(identifier.impl()) == child); 347 counterMaps().get(child->renderer())->remove(identifier.impl()); 348 if (!child->renderer()->documentBeingDestroyed()) { 349 RenderObjectChildList* children = child->renderer()->virtualChildren(); 350 if (children) 351 children->invalidateCounters(child->renderer(), identifier); 352 } 353 delete child; 354 } 355 RenderObject* renderer = node->renderer(); 356 if (!renderer->documentBeingDestroyed()) { 357 if (RenderObjectChildList* children = renderer->virtualChildren()) 358 children->invalidateCounters(renderer, identifier); 359 } 360 if (CounterNode* parent = node->parent()) 361 parent->removeChild(node, identifier); 362 delete node; 363} 364 365void RenderCounter::destroyCounterNodes(RenderObject* renderer) 366{ 367 CounterMaps& maps = counterMaps(); 368 CounterMaps::iterator mapsIterator = maps.find(renderer); 369 if (mapsIterator == maps.end()) 370 return; 371 CounterMap* map = mapsIterator->second; 372 CounterMap::const_iterator end = map->end(); 373 for (CounterMap::const_iterator it = map->begin(); it != end; ++it) { 374 AtomicString identifier(it->first.get()); 375 destroyCounterNodeWithoutMapRemoval(identifier, it->second); 376 } 377 maps.remove(mapsIterator); 378 delete map; 379 renderer->m_hasCounterNodeMap = false; 380} 381 382void RenderCounter::destroyCounterNode(RenderObject* renderer, const AtomicString& identifier) 383{ 384 CounterMap* map = counterMaps().get(renderer); 385 if (!map) 386 return; 387 CounterMap::iterator mapIterator = map->find(identifier.impl()); 388 if (mapIterator == map->end()) 389 return; 390 destroyCounterNodeWithoutMapRemoval(identifier, mapIterator->second); 391 map->remove(mapIterator); 392 // We do not delete "map" here even if empty because we expect to reuse 393 // it soon. In order for a renderer to lose all its counters permanently, 394 // a style change for the renderer involving removal of all counter 395 // directives must occur, in which case, RenderCounter::destroyCounterNodes() 396 // must be called. 397 // The destruction of the Renderer (possibly caused by the removal of its 398 // associated DOM node) is the other case that leads to the permanent 399 // destruction of all counters attached to a Renderer. In this case 400 // RenderCounter::destroyCounterNodes() must be and is now called, too. 401 // RenderCounter::destroyCounterNodes() handles destruction of the counter 402 // map associated with a renderer, so there is no risk in leaking the map. 403} 404 405static void updateCounters(RenderObject* renderer) 406{ 407 ASSERT(renderer->style()); 408 const CounterDirectiveMap* directiveMap = renderer->style()->counterDirectives(); 409 if (!directiveMap) 410 return; 411 CounterDirectiveMap::const_iterator end = directiveMap->end(); 412 if (!renderer->m_hasCounterNodeMap) { 413 for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) 414 makeCounterNode(renderer, AtomicString(it->first.get()), false); 415 return; 416 } 417 CounterMap* counterMap = counterMaps().get(renderer); 418 ASSERT(counterMap); 419 for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) { 420 CounterNode* node = counterMap->get(it->first.get()); 421 if (!node) { 422 makeCounterNode(renderer, AtomicString(it->first.get()), false); 423 continue; 424 } 425 CounterNode* newParent = 0; 426 CounterNode* newPreviousSibling; 427 findPlaceForCounter(renderer, AtomicString(it->first.get()), node->hasResetType(), newParent, newPreviousSibling); 428 CounterNode* parent = node->parent(); 429 if (newParent == parent && newPreviousSibling == node->previousSibling()) 430 continue; 431 if (parent) 432 parent->removeChild(node, it->first.get()); 433 if (newParent) 434 newParent->insertAfter(node, newPreviousSibling, it->first.get()); 435 } 436} 437 438void RenderCounter::rendererSubtreeAttached(RenderObject* renderer) 439{ 440 for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer)) 441 updateCounters(descendant); 442} 443 444void RenderCounter::rendererStyleChanged(RenderObject* renderer, const RenderStyle* oldStyle, const RenderStyle* newStyle) 445{ 446 const CounterDirectiveMap* newCounterDirectives; 447 const CounterDirectiveMap* oldCounterDirectives; 448 if (oldStyle && (oldCounterDirectives = oldStyle->counterDirectives())) { 449 if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) { 450 CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); 451 CounterDirectiveMap::const_iterator oldMapEnd = oldCounterDirectives->end(); 452 for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { 453 CounterDirectiveMap::const_iterator oldMapIt = oldCounterDirectives->find(it->first); 454 if (oldMapIt != oldMapEnd) { 455 if (oldMapIt->second == it->second) 456 continue; 457 RenderCounter::destroyCounterNode(renderer, it->first.get()); 458 } 459 // We must create this node here, because the changed node may be a node with no display such as 460 // as those created by the increment or reset directives and the re-layout that will happen will 461 // not catch the change if the node had no children. 462 makeCounterNode(renderer, it->first.get(), false); 463 } 464 // Destroying old counters that do not exist in the new counterDirective map. 465 for (CounterDirectiveMap::const_iterator it = oldCounterDirectives->begin(); it !=oldMapEnd; ++it) { 466 if (!newCounterDirectives->contains(it->first)) 467 RenderCounter::destroyCounterNode(renderer, it->first.get()); 468 } 469 } else { 470 if (renderer->m_hasCounterNodeMap) 471 RenderCounter::destroyCounterNodes(renderer); 472 } 473 } else if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) { 474 CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); 475 for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { 476 // We must create this node here, because the added node may be a node with no display such as 477 // as those created by the increment or reset directives and the re-layout that will happen will 478 // not catch the change if the node had no children. 479 makeCounterNode(renderer, it->first.get(), false); 480 } 481 } 482} 483 484} // namespace WebCore 485