webengine/osswebengine/WebCore/rendering/RenderCounter.cpp
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     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 
       
    33 namespace WebCore {
       
    34 
       
    35 using namespace HTMLNames;
       
    36 
       
    37 typedef HashMap<RefPtr<AtomicStringImpl>, CounterNode*> CounterMap;
       
    38 typedef HashMap<const RenderObject*, CounterMap*> CounterMaps;
       
    39 
       
    40 static CounterNode* counter(RenderObject*, const AtomicString& counterName, bool alwaysCreateCounter);
       
    41 
       
    42 static CounterMaps& counterMaps()
       
    43 {
       
    44     static CounterMaps staticCounterMaps;
       
    45     return staticCounterMaps;
       
    46 }
       
    47 
       
    48 static inline RenderObject* previousSiblingOrParent(RenderObject* object)
       
    49 {
       
    50     if (RenderObject* sibling = object->previousSibling())
       
    51         return sibling;
       
    52     return object->parent();
       
    53 }
       
    54 
       
    55 static CounterNode* lastDescendant(CounterNode* node)
       
    56 {
       
    57     CounterNode* last = node->lastChild();
       
    58     if (!last)
       
    59         return 0;
       
    60 
       
    61     while (CounterNode* lastChild = last->lastChild())
       
    62         last = lastChild;
       
    63 
       
    64     return last;
       
    65 }
       
    66 
       
    67 static CounterNode* previousInPreOrder(CounterNode* node)
       
    68 {
       
    69     CounterNode* previous = node->previousSibling();
       
    70     if (!previous)
       
    71         return node->parent();
       
    72 
       
    73     while (CounterNode* lastChild = previous->lastChild())
       
    74         previous = lastChild;
       
    75 
       
    76     return previous;
       
    77 }
       
    78 
       
    79 static bool planCounter(RenderObject* object, const AtomicString& counterName, bool& isReset, int& value)
       
    80 {
       
    81     ASSERT(object);
       
    82 
       
    83     // Real text nodes don't have their own style so they can't have counters.
       
    84     // We can't even look at their styles or we'll see extra resets and increments!
       
    85     if (object->isText() && !object->isBR())
       
    86         return false;
       
    87 
       
    88     RenderStyle* style = object->style();
       
    89     ASSERT(style);
       
    90 
       
    91     if (const CounterDirectiveMap* directivesMap = style->counterDirectives()) {
       
    92         CounterDirectives directives = directivesMap->get(counterName.impl());
       
    93         if (directives.m_reset) {
       
    94             value = directives.m_resetValue;
       
    95             if (directives.m_increment)
       
    96                 value += directives.m_incrementValue;
       
    97             isReset = true;
       
    98             return true;
       
    99         }
       
   100         if (directives.m_increment) {
       
   101             value = directives.m_incrementValue;
       
   102             isReset = false;
       
   103             return true;
       
   104         }
       
   105     }
       
   106 
       
   107     if (counterName == "list-item") {
       
   108         if (object->isListItem()) {
       
   109             if (static_cast<RenderListItem*>(object)->hasExplicitValue()) {
       
   110                 value = static_cast<RenderListItem*>(object)->explicitValue();
       
   111                 isReset = true;
       
   112                 return true;
       
   113             }
       
   114             value = 1;
       
   115             isReset = false;
       
   116             return true;
       
   117         }
       
   118         if (Node* e = object->element()) {
       
   119             if (e->hasTagName(olTag)) {
       
   120                 value = static_cast<HTMLOListElement*>(e)->start();
       
   121                 isReset = true;
       
   122                 return true;
       
   123             }
       
   124             if (e->hasTagName(ulTag) || e->hasTagName(menuTag) || e->hasTagName(dirTag)) {
       
   125                 value = 0;
       
   126                 isReset = true;
       
   127                 return true;
       
   128             }
       
   129         }
       
   130     }
       
   131 
       
   132     return false;
       
   133 }
       
   134 
       
   135 static bool findPlaceForCounter(RenderObject* object, const AtomicString& counterName,
       
   136     bool isReset, CounterNode*& parent, CounterNode*& previousSibling)
       
   137 {
       
   138     // Find the appropriate previous sibling for insertion into the parent node
       
   139     // by searching in render tree order for a child of the counter.
       
   140     parent = 0;
       
   141     previousSibling = 0;
       
   142     RenderObject* resetCandidate = isReset ? object->parent() : previousSiblingOrParent(object);
       
   143     RenderObject* prevCounterCandidate = object;
       
   144     CounterNode* candidateCounter = 0;
       
   145     while ((prevCounterCandidate = prevCounterCandidate->previousInPreOrder())) {
       
   146         CounterNode* c = counter(prevCounterCandidate, counterName, false);
       
   147         if (prevCounterCandidate == resetCandidate) {
       
   148             if (!candidateCounter)
       
   149                 candidateCounter = c;
       
   150             if (candidateCounter) {
       
   151                 if (candidateCounter->isReset()) {
       
   152                     parent = candidateCounter;
       
   153                     previousSibling = 0;
       
   154                 } else {
       
   155                     parent = candidateCounter->parent();
       
   156                     previousSibling = candidateCounter;
       
   157                 }
       
   158                 return true;
       
   159             }
       
   160             resetCandidate = previousSiblingOrParent(resetCandidate);
       
   161         } else if (c) {
       
   162             if (c->isReset())
       
   163                 candidateCounter = 0;
       
   164             else if (!candidateCounter)
       
   165                 candidateCounter = c;
       
   166         }
       
   167     }
       
   168 
       
   169     return false;
       
   170 }
       
   171 
       
   172 static CounterNode* counter(RenderObject* object, const AtomicString& counterName, bool alwaysCreateCounter)
       
   173 {
       
   174     ASSERT(object);
       
   175 
       
   176     if (object->m_hasCounterNodeMap)
       
   177         if (CounterMap* nodeMap = counterMaps().get(object))
       
   178             if (CounterNode* node = nodeMap->get(counterName.impl()))
       
   179                 return node;
       
   180 
       
   181     bool isReset = false;
       
   182     int value = 0;
       
   183     if (!planCounter(object, counterName, isReset, value) && !alwaysCreateCounter)
       
   184         return 0;
       
   185 
       
   186     CounterNode* newParent = 0;
       
   187     CounterNode* newPreviousSibling = 0;
       
   188     CounterNode* newNode;
       
   189     if (findPlaceForCounter(object, counterName, isReset, newParent, newPreviousSibling)) {
       
   190         newNode = new CounterNode(object, isReset, value);
       
   191         newParent->insertAfter(newNode, newPreviousSibling);
       
   192     } else {
       
   193         // Make a reset node for counters that aren't inside an existing reset node.
       
   194         newNode = new CounterNode(object, true, value);
       
   195     }
       
   196 
       
   197     CounterMap* nodeMap;
       
   198     if (object->m_hasCounterNodeMap)
       
   199         nodeMap = counterMaps().get(object);
       
   200     else {
       
   201         nodeMap = new CounterMap;
       
   202         counterMaps().set(object, nodeMap);
       
   203         object->m_hasCounterNodeMap = true;
       
   204     }
       
   205     nodeMap->set(counterName.impl(), newNode);
       
   206 
       
   207     return newNode;
       
   208 }
       
   209 
       
   210 RenderCounter::RenderCounter(Document* node, const CounterContent& counter)
       
   211     : RenderText(node, StringImpl::empty())
       
   212     , m_counter(counter)
       
   213     , m_counterNode(0)
       
   214 {
       
   215 }
       
   216 
       
   217 const char* RenderCounter::renderName() const
       
   218 {
       
   219     return "RenderCounter";
       
   220 }
       
   221 
       
   222 bool RenderCounter::isRenderCounter() const
       
   223 {
       
   224     return true;
       
   225 }
       
   226 
       
   227 PassRefPtr<StringImpl> RenderCounter::originalText() const
       
   228 {
       
   229     if (!parent())
       
   230         return 0;
       
   231 
       
   232     if (!m_counterNode)
       
   233         m_counterNode = counter(parent(), m_counter.identifier(), true);
       
   234 
       
   235     CounterNode* child = m_counterNode;
       
   236     int value = child->isReset() ? child->value() : child->countInParent();
       
   237 
       
   238     String text = listMarkerText(m_counter.listStyle(), value);
       
   239 
       
   240     if (!m_counter.separator().isNull()) {
       
   241         if (!child->isReset())
       
   242             child = child->parent();
       
   243         while (CounterNode* parent = child->parent()) {
       
   244             text = listMarkerText(m_counter.listStyle(), child->countInParent())
       
   245                 + m_counter.separator() + text;
       
   246             child = parent;
       
   247         }
       
   248     }
       
   249 
       
   250     return text.impl();
       
   251 }
       
   252 
       
   253 void RenderCounter::dirtyLineBoxes(bool fullLayout, bool dummy)
       
   254 {
       
   255     if (prefWidthsDirty())
       
   256         calcPrefWidths(0);
       
   257     RenderText::dirtyLineBoxes(fullLayout, dummy);
       
   258 }
       
   259 
       
   260 void RenderCounter::calcPrefWidths(int lead)
       
   261 {
       
   262     setTextInternal(originalText());
       
   263     RenderText::calcPrefWidths(lead);
       
   264 }
       
   265 
       
   266 static void destroyCounterNodeChildren(AtomicStringImpl* identifier, CounterNode* node)
       
   267 {
       
   268     CounterNode* previous;
       
   269     for (CounterNode* child = lastDescendant(node); child && child != node; child = previous) {
       
   270         previous = previousInPreOrder(child);
       
   271         child->parent()->removeChild(child);
       
   272         ASSERT(counterMaps().get(child->renderer())->get(identifier) == child);
       
   273         counterMaps().get(child->renderer())->remove(identifier);
       
   274         delete child;
       
   275     }
       
   276 }
       
   277 
       
   278 void RenderCounter::destroyCounterNodes(RenderObject* object)
       
   279 {
       
   280     CounterMaps& maps = counterMaps();
       
   281     CounterMap* map = maps.get(object);
       
   282     if (!map)
       
   283         return;
       
   284     maps.remove(object);
       
   285 
       
   286     CounterMap::const_iterator end = map->end();
       
   287     for (CounterMap::const_iterator it = map->begin(); it != end; ++it) {
       
   288         CounterNode* node = it->second;
       
   289         destroyCounterNodeChildren(it->first.get(), node);
       
   290         if (CounterNode* parent = node->parent())
       
   291             parent->removeChild(node);
       
   292         delete node;
       
   293     }
       
   294 
       
   295     delete map;
       
   296 }
       
   297 
       
   298 } // namespace WebCore