static void updateCounters(RenderObject* renderer) { ASSERT(renderer->style()); const CounterDirectiveMap* directiveMap = renderer->style()->counterDirectives(); if (!directiveMap) return; CounterDirectiveMap::const_iterator end = directiveMap->end(); if (!renderer->hasCounterNodeMap()) { for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) makeCounterNode(renderer, it->key, false); return; } CounterMap* counterMap = counterMaps().get(renderer); ASSERT(counterMap); for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) { RefPtr<CounterNode> node = counterMap->get(it->key); if (!node) { makeCounterNode(renderer, it->key, false); continue; } RefPtr<CounterNode> newParent = 0; RefPtr<CounterNode> newPreviousSibling = 0; findPlaceForCounter(renderer, it->key, node->hasResetType(), newParent, newPreviousSibling); if (node != counterMap->get(it->key)) continue; CounterNode* parent = node->parent(); if (newParent == parent && newPreviousSibling == node->previousSibling()) continue; if (parent) parent->removeChild(node.get()); if (newParent) newParent->insertAfter(node.get(), newPreviousSibling.get(), it->key); } }
PassRefPtr<StringImpl> RenderCounter::originalText() const { if (!m_counterNode) { RenderObject* beforeAfterContainer = parent(); while (true) { if (!beforeAfterContainer) return 0; if (!beforeAfterContainer->isAnonymous()) return 0; // RenderCounters are restricted to before and after pseudo elements PseudoId containerStyle = beforeAfterContainer->style()->styleType(); if ((containerStyle == BEFORE) || (containerStyle == AFTER)) break; beforeAfterContainer = beforeAfterContainer->parent(); } makeCounterNode(beforeAfterContainer, m_counter.identifier(), true)->addRenderer(const_cast<RenderCounter*>(this)); ASSERT(m_counterNode); } CounterNode* child = m_counterNode; int value = child->actsAsReset() ? child->value() : child->countInParent(); String text = listMarkerText(m_counter.listStyle(), value); if (!m_counter.separator().isNull()) { if (!child->actsAsReset()) child = child->parent(); while (CounterNode* parent = child->parent()) { text = listMarkerText(m_counter.listStyle(), child->countInParent()) + m_counter.separator() + text; child = parent; } } return text.impl(); }
PassRefPtr<StringImpl> RenderCounter::originalText() const { if (!parent()) return 0; if (!m_counterNode) m_counterNode = makeCounterNode(parent(), m_counter.identifier(), true); CounterNode* child = m_counterNode; int value = child->actsAsReset() ? child->value() : child->countInParent(); String text = listMarkerText(m_counter.listStyle(), value); if (!m_counter.separator().isNull()) { if (!child->actsAsReset()) child = child->parent(); while (CounterNode* parent = child->parent()) { text = listMarkerText(m_counter.listStyle(), child->countInParent()) + m_counter.separator() + text; child = parent; } } return text.impl(); }
void LayoutCounter::layoutObjectStyleChanged(LayoutObject& layoutObject, const ComputedStyle* oldStyle, const ComputedStyle& newStyle) { Node* node = layoutObject.generatingNode(); if (!node || node->needsAttach()) return; // cannot have generated content or if it can have, it will be handled during attaching const CounterDirectiveMap* oldCounterDirectives = oldStyle ? oldStyle->counterDirectives() : 0; const CounterDirectiveMap* newCounterDirectives = newStyle.counterDirectives(); if (oldCounterDirectives) { if (newCounterDirectives) { CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); CounterDirectiveMap::const_iterator oldMapEnd = oldCounterDirectives->end(); for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { CounterDirectiveMap::const_iterator oldMapIt = oldCounterDirectives->find(it->key); if (oldMapIt != oldMapEnd) { if (oldMapIt->value == it->value) continue; LayoutCounter::destroyCounterNode(layoutObject, it->key); } // We must create this node here, because the changed node may be a node with no display such as // as those created by the increment or reset directives and the re-layout that will happen will // not catch the change if the node had no children. makeCounterNode(layoutObject, it->key, false); } // Destroying old counters that do not exist in the new counterDirective map. for (CounterDirectiveMap::const_iterator it = oldCounterDirectives->begin(); it !=oldMapEnd; ++it) { if (!newCounterDirectives->contains(it->key)) LayoutCounter::destroyCounterNode(layoutObject, it->key); } } else { if (layoutObject.hasCounterNodeMap()) LayoutCounter::destroyCounterNodes(layoutObject); } } else if (newCounterDirectives) { if (layoutObject.hasCounterNodeMap()) LayoutCounter::destroyCounterNodes(layoutObject); CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { // We must create this node here, because the added node may be a node with no display such as // as those created by the increment or reset directives and the re-layout that will happen will // not catch the change if the node had no children. makeCounterNode(layoutObject, it->key, false); } } }
void RenderCounter::rendererStyleChanged(RenderObject* renderer, const RenderStyle* oldStyle, const RenderStyle* newStyle) { Node* node = renderer->generatingNode(); if (!node || !node->attached()) return; // cannot have generated content or if it can have, it will be handled during attaching const CounterDirectiveMap* newCounterDirectives; const CounterDirectiveMap* oldCounterDirectives; if (oldStyle && (oldCounterDirectives = oldStyle->counterDirectives())) { if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) { CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); CounterDirectiveMap::const_iterator oldMapEnd = oldCounterDirectives->end(); for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { CounterDirectiveMap::const_iterator oldMapIt = oldCounterDirectives->find(it->first); if (oldMapIt != oldMapEnd) { if (oldMapIt->second == it->second) continue; RenderCounter::destroyCounterNode(renderer, it->first.get()); } // We must create this node here, because the changed node may be a node with no display such as // as those created by the increment or reset directives and the re-layout that will happen will // not catch the change if the node had no children. makeCounterNode(renderer, it->first.get(), false); } // Destroying old counters that do not exist in the new counterDirective map. for (CounterDirectiveMap::const_iterator it = oldCounterDirectives->begin(); it !=oldMapEnd; ++it) { if (!newCounterDirectives->contains(it->first)) RenderCounter::destroyCounterNode(renderer, it->first.get()); } } else { if (renderer->hasCounterNodeMap()) RenderCounter::destroyCounterNodes(renderer); } } else if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) { CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { // We must create this node here, because the added node may be a node with no display such as // as those created by the increment or reset directives and the re-layout that will happen will // not catch the change if the node had no children. makeCounterNode(renderer, it->first.get(), false); } } }
static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& identifier, bool isReset, RefPtr<CounterNode>& parent, RefPtr<CounterNode>& previousSibling) { // We cannot stop searching for counters with the same identifier before we also // check this renderer, because it may affect the positioning in the tree of our counter. RenderObject* searchEndRenderer = previousSiblingOrParent(counterOwner); // We check renderers in preOrder from the renderer that our counter is attached to // towards the begining of the document for counters with the same identifier as the one // we are trying to find a place for. This is the next renderer to be checked. RenderObject* currentRenderer = previousInPreOrder(counterOwner); previousSibling = 0; RefPtr<CounterNode> previousSiblingProtector = 0; while (currentRenderer) { CounterNode* currentCounter = makeCounterNode(currentRenderer, identifier, false); if (searchEndRenderer == currentRenderer) { // We may be at the end of our search. if (currentCounter) { // We have a suitable counter on the EndSearchRenderer. if (previousSiblingProtector) { // But we already found another counter that we come after. if (currentCounter->actsAsReset()) { // We found a reset counter that is on a renderer that is a sibling of ours or a parent. if (isReset && areRenderersElementsSiblings(currentRenderer, counterOwner)) { // We are also a reset counter and the previous reset was on a sibling renderer // hence we are the next sibling of that counter if that reset is not a root or // we are a root node if that reset is a root. parent = currentCounter->parent(); previousSibling = parent ? currentCounter : 0; return parent; } // We are not a reset node or the previous reset must be on an ancestor of our owner renderer // hence we must be a child of that reset counter. parent = currentCounter; // In some cases renders can be reparented (ex. nodes inside a table but not in a column or row). // In these cases the identified previousSibling will be invalid as its parent is different from // our identified parent. if (previousSiblingProtector->parent() != currentCounter) previousSiblingProtector = 0; previousSibling = previousSiblingProtector.get(); return true; } // CurrentCounter, the counter at the EndSearchRenderer, is not reset. if (!isReset || !areRenderersElementsSiblings(currentRenderer, counterOwner)) { // If the node we are placing is not reset or we have found a counter that is attached // to an ancestor of the placed counter's owner renderer we know we are a sibling of that node. if (currentCounter->parent() != previousSiblingProtector->parent()) return false; parent = currentCounter->parent(); previousSibling = previousSiblingProtector.get(); return true; } } else { // We are at the potential end of the search, but we had no previous sibling candidate // In this case we follow pretty much the same logic as above but no ASSERTs about // previousSibling, and when we are a sibling of the end counter we must set previousSibling // to currentCounter. if (currentCounter->actsAsReset()) { if (isReset && areRenderersElementsSiblings(currentRenderer, counterOwner)) { parent = currentCounter->parent(); previousSibling = currentCounter; return parent; } parent = currentCounter; previousSibling = previousSiblingProtector.get(); return true; } if (!isReset || !areRenderersElementsSiblings(currentRenderer, counterOwner)) { parent = currentCounter->parent(); previousSibling = currentCounter; return true; } previousSiblingProtector = currentCounter; } } // We come here if the previous sibling or parent of our owner renderer had no // good counter, or we are a reset node and the counter on the previous sibling // of our owner renderer was not a reset counter. // Set a new goal for the end of the search. searchEndRenderer = previousSiblingOrParent(currentRenderer); } else { // We are searching descendants of a previous sibling of the renderer that the // counter being placed is attached to. if (currentCounter) { // We found a suitable counter. if (previousSiblingProtector) { // Since we had a suitable previous counter before, we should only consider this one as our // previousSibling if it is a reset counter and hence the current previousSibling is its child. if (currentCounter->actsAsReset()) { previousSiblingProtector = currentCounter; // We are no longer interested in previous siblings of the currentRenderer or their children // as counters they may have attached cannot be the previous sibling of the counter we are placing. currentRenderer = parentElement(currentRenderer)->renderer(); continue; } } else previousSiblingProtector = currentCounter; currentRenderer = previousSiblingOrParent(currentRenderer); continue; } } // This function is designed so that the same test is not done twice in an iteration, except for this one // which may be done twice in some cases. Rearranging the decision points though, to accommodate this // performance improvement would create more code duplication than is worthwhile in my oppinion and may further // impede the readability of this already complex algorithm. if (previousSiblingProtector) currentRenderer = previousSiblingOrParent(currentRenderer); else currentRenderer = previousInPreOrder(currentRenderer); } return false; }