webengine/osswebengine/WebCore/rendering/RenderCounter.cpp
changeset 0 dd21522fd290
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/WebCore/rendering/RenderCounter.cpp	Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,298 @@
+/**
+ * Copyright (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com)
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "RenderCounter.h"
+
+#include "CounterNode.h"
+#include "Document.h"
+#include "HTMLNames.h"
+#include "HTMLOListElement.h"
+#include "RenderListItem.h"
+#include "RenderListMarker.h"
+#include "RenderStyle.h"
+
+namespace WebCore {
+
+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 CounterMaps& counterMaps()
+{
+    static CounterMaps staticCounterMaps;
+    return staticCounterMaps;
+}
+
+static inline RenderObject* previousSiblingOrParent(RenderObject* object)
+{
+    if (RenderObject* sibling = object->previousSibling())
+        return sibling;
+    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);
+
+    // Real text nodes don't have their own style so they can't have counters.
+    // We can't even look at their styles or we'll see extra resets and increments!
+    if (object->isText() && !object->isBR())
+        return false;
+
+    RenderStyle* style = object->style();
+    ASSERT(style);
+
+    if (const CounterDirectiveMap* directivesMap = style->counterDirectives()) {
+        CounterDirectives directives = directivesMap->get(counterName.impl());
+        if (directives.m_reset) {
+            value = directives.m_resetValue;
+            if (directives.m_increment)
+                value += directives.m_incrementValue;
+            isReset = true;
+            return true;
+        }
+        if (directives.m_increment) {
+            value = directives.m_incrementValue;
+            isReset = false;
+            return true;
+        }
+    }
+
+    if (counterName == "list-item") {
+        if (object->isListItem()) {
+            if (static_cast<RenderListItem*>(object)->hasExplicitValue()) {
+                value = static_cast<RenderListItem*>(object)->explicitValue();
+                isReset = true;
+                return true;
+            }
+            value = 1;
+            isReset = false;
+            return true;
+        }
+        if (Node* e = object->element()) {
+            if (e->hasTagName(olTag)) {
+                value = static_cast<HTMLOListElement*>(e)->start();
+                isReset = true;
+                return true;
+            }
+            if (e->hasTagName(ulTag) || e->hasTagName(menuTag) || e->hasTagName(dirTag)) {
+                value = 0;
+                isReset = true;
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+static bool findPlaceForCounter(RenderObject* object, const AtomicString& counterName,
+    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;
+    previousSibling = 0;
+    RenderObject* resetCandidate = isReset ? object->parent() : previousSiblingOrParent(object);
+    RenderObject* prevCounterCandidate = object;
+    CounterNode* candidateCounter = 0;
+    while ((prevCounterCandidate = prevCounterCandidate->previousInPreOrder())) {
+        CounterNode* c = counter(prevCounterCandidate, counterName, false);
+        if (prevCounterCandidate == resetCandidate) {
+            if (!candidateCounter)
+                candidateCounter = c;
+            if (candidateCounter) {
+                if (candidateCounter->isReset()) {
+                    parent = candidateCounter;
+                    previousSibling = 0;
+                } else {
+                    parent = candidateCounter->parent();
+                    previousSibling = candidateCounter;
+                }
+                return true;
+            }
+            resetCandidate = previousSiblingOrParent(resetCandidate);
+        } else if (c) {
+            if (c->isReset())
+                candidateCounter = 0;
+            else if (!candidateCounter)
+                candidateCounter = c;
+        }
+    }
+
+    return false;
+}
+
+static CounterNode* counter(RenderObject* object, const AtomicString& counterName, bool alwaysCreateCounter)
+{
+    ASSERT(object);
+
+    if (object->m_hasCounterNodeMap)
+        if (CounterMap* nodeMap = counterMaps().get(object))
+            if (CounterNode* node = nodeMap->get(counterName.impl()))
+                return node;
+
+    bool isReset = false;
+    int value = 0;
+    if (!planCounter(object, counterName, isReset, value) && !alwaysCreateCounter)
+        return 0;
+
+    CounterNode* newParent = 0;
+    CounterNode* newPreviousSibling = 0;
+    CounterNode* newNode;
+    if (findPlaceForCounter(object, counterName, isReset, newParent, newPreviousSibling)) {
+        newNode = new CounterNode(object, isReset, value);
+        newParent->insertAfter(newNode, newPreviousSibling);
+    } else {
+        // Make a reset node for counters that aren't inside an existing reset node.
+        newNode = new CounterNode(object, true, value);
+    }
+
+    CounterMap* nodeMap;
+    if (object->m_hasCounterNodeMap)
+        nodeMap = counterMaps().get(object);
+    else {
+        nodeMap = new CounterMap;
+        counterMaps().set(object, nodeMap);
+        object->m_hasCounterNodeMap = true;
+    }
+    nodeMap->set(counterName.impl(), newNode);
+
+    return newNode;
+}
+
+RenderCounter::RenderCounter(Document* node, const CounterContent& counter)
+    : RenderText(node, StringImpl::empty())
+    , m_counter(counter)
+    , m_counterNode(0)
+{
+}
+
+const char* RenderCounter::renderName() const
+{
+    return "RenderCounter";
+}
+
+bool RenderCounter::isRenderCounter() const
+{
+    return true;
+}
+
+PassRefPtr<StringImpl> RenderCounter::originalText() const
+{
+    if (!parent())
+        return 0;
+
+    if (!m_counterNode)
+        m_counterNode = counter(parent(), m_counter.identifier(), true);
+
+    CounterNode* child = m_counterNode;
+    int value = child->isReset() ? child->value() : child->countInParent();
+
+    String text = listMarkerText(m_counter.listStyle(), value);
+
+    if (!m_counter.separator().isNull()) {
+        if (!child->isReset())
+            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 RenderCounter::dirtyLineBoxes(bool fullLayout, bool dummy)
+{
+    if (prefWidthsDirty())
+        calcPrefWidths(0);
+    RenderText::dirtyLineBoxes(fullLayout, dummy);
+}
+
+void RenderCounter::calcPrefWidths(int lead)
+{
+    setTextInternal(originalText());
+    RenderText::calcPrefWidths(lead);
+}
+
+static void destroyCounterNodeChildren(AtomicStringImpl* 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);
+        delete child;
+    }
+}
+
+void RenderCounter::destroyCounterNodes(RenderObject* object)
+{
+    CounterMaps& maps = counterMaps();
+    CounterMap* map = maps.get(object);
+    if (!map)
+        return;
+    maps.remove(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);
+        if (CounterNode* parent = node->parent())
+            parent->removeChild(node);
+        delete node;
+    }
+
+    delete map;
+}
+
+} // namespace WebCore