summaryrefslogtreecommitdiffstats
path: root/WebCore/rendering/RenderCounter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/rendering/RenderCounter.cpp')
-rw-r--r--WebCore/rendering/RenderCounter.cpp204
1 files changed, 124 insertions, 80 deletions
diff --git a/WebCore/rendering/RenderCounter.cpp b/WebCore/rendering/RenderCounter.cpp
index 17c6dad..b0aefc5 100644
--- a/WebCore/rendering/RenderCounter.cpp
+++ b/WebCore/rendering/RenderCounter.cpp
@@ -38,7 +38,7 @@ using namespace HTMLNames;
typedef HashMap<RefPtr<AtomicStringImpl>, CounterNode*> CounterMap;
typedef HashMap<const RenderObject*, CounterMap*> CounterMaps;
-static CounterNode* counter(RenderObject*, const AtomicString& counterName, bool alwaysCreateCounter);
+static CounterNode* makeCounterNode(RenderObject*, const AtomicString& counterName, bool alwaysCreateCounter);
static CounterMaps& counterMaps()
{
@@ -53,30 +53,6 @@ static inline RenderObject* previousSiblingOrParent(RenderObject* object)
return object->parent();
}
-static CounterNode* lastDescendant(CounterNode* node)
-{
- CounterNode* last = node->lastChild();
- if (!last)
- return 0;
-
- while (CounterNode* lastChild = last->lastChild())
- last = lastChild;
-
- return last;
-}
-
-static CounterNode* previousInPreOrder(CounterNode* node)
-{
- CounterNode* previous = node->previousSibling();
- if (!previous)
- return node->parent();
-
- while (CounterNode* lastChild = previous->lastChild())
- previous = lastChild;
-
- return previous;
-}
-
static bool planCounter(RenderObject* object, const AtomicString& counterName, bool& isReset, int& value)
{
ASSERT(object);
@@ -133,59 +109,124 @@ static bool planCounter(RenderObject* object, const AtomicString& counterName, b
return false;
}
-static bool findPlaceForCounter(RenderObject* object, const AtomicString& counterName,
- bool isReset, CounterNode*& parent, CounterNode*& previousSibling)
+// - Finds the insertion point for the counter described by counterOwner, isReset and
+// identifier in the CounterNode tree for identifier and sets parent and
+// previousSibling accordingly.
+// - The function returns true if the counter whose insertion point is searched is NOT
+// the root of the tree.
+// - The root of the tree is a counter reference that is not in the scope of any other
+// counter with the same identifier.
+// - All the counter references with the same identifier as this one that are in
+// children or subsequent siblings of the renderer that owns the root of the tree
+// form the rest of of the nodes of the tree.
+// - The root of the tree is always a reset type reference.
+// - A subtree rooted at any reset node in the tree is equivalent to all counter
+// references that are in the scope of the counter or nested counter defined by that
+// reset node.
+// - Non-reset CounterNodes cannot have descendants.
+
+static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& identifier, bool isReset, CounterNode*& parent, CounterNode*& previousSibling)
{
- // Find the appropriate previous sibling for insertion into the parent node
- // by searching in render tree order for a child of the counter.
- parent = 0;
+ // 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 = counterOwner->previousInPreOrder();
previousSibling = 0;
- RenderObject* resetCandidate = isReset ? object->parent() : previousSiblingOrParent(object);
- RenderObject* prevCounterCandidate = object;
- CounterNode* candidateCounter = 0;
- // When a reset counter is chosen as candidateCounter, we'll
- // decide the new node should be a child of the reset node or a
- // sibling or the reset node. This flag controls it.
- bool createChildForReset = true;
- while ((prevCounterCandidate = prevCounterCandidate->previousInPreOrder())) {
- CounterNode* c = counter(prevCounterCandidate, counterName, false);
- if (prevCounterCandidate == resetCandidate) {
- if (!candidateCounter) {
- candidateCounter = c;
- createChildForReset = true;
- }
- if (candidateCounter) {
- if (createChildForReset && candidateCounter->isReset()) {
- parent = candidateCounter;
- previousSibling = 0;
- } else {
- parent = candidateCounter->parent();
- previousSibling = candidateCounter;
+ 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 (previousSibling) { // But we already found another counter that we come after.
+ if (currentCounter->isReset()) {
+ // We found a reset counter that is on a renderer that is a sibling of ours or a parent.
+ if (isReset && currentRenderer->parent() == counterOwner->parent()) {
+ // 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 renderer
+ // hence we must be a child of that reset counter.
+ parent = currentCounter;
+ ASSERT(previousSibling->parent() == currentCounter);
+ return true;
+ }
+ // CurrentCounter, the counter at the EndSearchRenderer, is not reset.
+ if (!isReset || currentRenderer->parent() != counterOwner->parent()) {
+ // 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 renderer we know we are a sibling of that node.
+ ASSERT(currentCounter->parent() == previousSibling->parent());
+ parent = currentCounter->parent();
+ 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->isReset()) {
+ if (isReset && currentRenderer->parent() == counterOwner->parent()) {
+ parent = currentCounter->parent();
+ previousSibling = currentCounter;
+ return parent;
+ }
+ parent = currentCounter;
+ return true;
+ }
+ if (!isReset || currentRenderer->parent() != counterOwner->parent()) {
+ parent = currentCounter->parent();
+ previousSibling = currentCounter;
+ return true;
+ }
+ previousSibling = currentCounter;
}
- return true;
}
- resetCandidate = previousSiblingOrParent(resetCandidate);
- } else if (c) {
- if (c->isReset()) {
- if (c->parent()) {
- // The new node may be the next sibling of this reset node.
- createChildForReset = false;
- candidateCounter = c;
- } else {
- createChildForReset = true;
- candidateCounter = 0;
- }
- } else if (!candidateCounter) {
- createChildForReset = true;
- candidateCounter = c;
+ // We come here if the previous sibling or parent of our renderer had no
+ // good counter, or we are a reset node and the counter on the previous sibling
+ // of our 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 (previousSibling) {
+ // 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->isReset()) {
+ previousSibling = 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 = currentRenderer->parent();
+ continue;
+ }
+ } else
+ previousSibling = 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 (previousSibling)
+ currentRenderer = previousSiblingOrParent(currentRenderer);
+ else
+ currentRenderer = currentRenderer->previousInPreOrder();
}
-
return false;
}
-static CounterNode* counter(RenderObject* object, const AtomicString& counterName, bool alwaysCreateCounter)
+static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& counterName, bool alwaysCreateCounter)
{
ASSERT(object);
@@ -204,7 +245,7 @@ static CounterNode* counter(RenderObject* object, const AtomicString& counterNam
CounterNode* newNode;
if (findPlaceForCounter(object, counterName, isReset, newParent, newPreviousSibling)) {
newNode = new CounterNode(object, isReset, value);
- newParent->insertAfter(newNode, newPreviousSibling);
+ newParent->insertAfter(newNode, newPreviousSibling, counterName);
} else {
// Make a reset node for counters that aren't inside an existing reset node.
newNode = new CounterNode(object, true, value);
@@ -246,7 +287,7 @@ PassRefPtr<StringImpl> RenderCounter::originalText() const
return 0;
if (!m_counterNode)
- m_counterNode = counter(parent(), m_counter.identifier(), true);
+ m_counterNode = makeCounterNode(parent(), m_counter.identifier(), true);
CounterNode* child = m_counterNode;
int value = child->isReset() ? child->value() : child->countInParent();
@@ -272,24 +313,26 @@ void RenderCounter::calcPrefWidths(int lead)
RenderText::calcPrefWidths(lead);
}
-void RenderCounter::invalidate()
+void RenderCounter::invalidate(const AtomicString& identifier)
{
+ if (m_counter.identifier() != identifier)
+ return;
m_counterNode = 0;
setNeedsLayoutAndPrefWidthsRecalc();
}
-static void destroyCounterNodeChildren(AtomicStringImpl* identifier, CounterNode* node)
+static void destroyCounterNodeChildren(const AtomicString& identifier, CounterNode* node)
{
CounterNode* previous;
- for (CounterNode* child = lastDescendant(node); child && child != node; child = previous) {
- previous = previousInPreOrder(child);
- child->parent()->removeChild(child);
- ASSERT(counterMaps().get(child->renderer())->get(identifier) == child);
- counterMaps().get(child->renderer())->remove(identifier);
+ for (CounterNode* child = node->lastDescendant(); child && child != node; child = previous) {
+ previous = child->previousInPreOrder();
+ child->parent()->removeChild(child, identifier);
+ ASSERT(counterMaps().get(child->renderer())->get(identifier.impl()) == child);
+ counterMaps().get(child->renderer())->remove(identifier.impl());
if (!child->renderer()->documentBeingDestroyed()) {
RenderObjectChildList* children = child->renderer()->virtualChildren();
if (children)
- children->invalidateCounters(child->renderer());
+ children->invalidateCounters(child->renderer(), identifier);
}
delete child;
}
@@ -306,9 +349,10 @@ void RenderCounter::destroyCounterNodes(RenderObject* object)
CounterMap::const_iterator end = map->end();
for (CounterMap::const_iterator it = map->begin(); it != end; ++it) {
CounterNode* node = it->second;
- destroyCounterNodeChildren(it->first.get(), node);
+ AtomicString identifier(it->first.get());
+ destroyCounterNodeChildren(identifier, node);
if (CounterNode* parent = node->parent())
- parent->removeChild(node);
+ parent->removeChild(node, identifier);
delete node;
}