• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
7  * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  *
24  */
25 
26 #include "config.h"
27 #include "RenderListItem.h"
28 
29 #include "CachedImage.h"
30 #include "HTMLNames.h"
31 #include "HTMLOListElement.h"
32 #include "RenderListMarker.h"
33 #include "RenderView.h"
34 #include <wtf/StdLibExtras.h>
35 
36 using namespace std;
37 
38 namespace WebCore {
39 
40 using namespace HTMLNames;
41 
RenderListItem(Node * node)42 RenderListItem::RenderListItem(Node* node)
43     : RenderBlock(node)
44     , m_marker(0)
45     , m_hasExplicitValue(false)
46     , m_isValueUpToDate(false)
47     , m_notInList(false)
48 {
49     setInline(false);
50 }
51 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)52 void RenderListItem::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
53 {
54     RenderBlock::styleDidChange(diff, oldStyle);
55 
56     if (style()->listStyleType() != LNONE ||
57         (style()->listStyleImage() && !style()->listStyleImage()->errorOccurred())) {
58         RefPtr<RenderStyle> newStyle = RenderStyle::create();
59         // The marker always inherits from the list item, regardless of where it might end
60         // up (e.g., in some deeply nested line box). See CSS3 spec.
61         newStyle->inheritFrom(style());
62         if (!m_marker)
63             m_marker = new (renderArena()) RenderListMarker(this);
64         m_marker->setStyle(newStyle.release());
65     } else if (m_marker) {
66         m_marker->destroy();
67         m_marker = 0;
68     }
69 }
70 
destroy()71 void RenderListItem::destroy()
72 {
73     if (m_marker) {
74         m_marker->destroy();
75         m_marker = 0;
76     }
77     RenderBlock::destroy();
78 }
79 
enclosingList(Node * node)80 static Node* enclosingList(Node* node)
81 {
82     Node* parent = node->parentNode();
83     for (Node* n = parent; n; n = n->parentNode())
84         if (n->hasTagName(ulTag) || n->hasTagName(olTag))
85             return n;
86     // If there's no actual <ul> or <ol> list element, then our parent acts as
87     // our list for purposes of determining what other list items should be
88     // numbered as part of the same list.
89     return parent;
90 }
91 
previousListItem(Node * list,const RenderListItem * item)92 static RenderListItem* previousListItem(Node* list, const RenderListItem* item)
93 {
94     for (Node* node = item->node()->traversePreviousNode(); node != list; node = node->traversePreviousNode()) {
95         RenderObject* renderer = node->renderer();
96         if (!renderer || !renderer->isListItem())
97             continue;
98         Node* otherList = enclosingList(node);
99         // This item is part of our current list, so it's what we're looking for.
100         if (list == otherList)
101             return toRenderListItem(renderer);
102         // We found ourself inside another list; lets skip the rest of it.
103         // Use traverseNextNode() here because the other list itself may actually
104         // be a list item itself. We need to examine it, so we do this to counteract
105         // the traversePreviousNode() that will be done by the loop.
106         if (otherList)
107             node = otherList->traverseNextNode();
108     }
109     return 0;
110 }
111 
calcValue() const112 inline int RenderListItem::calcValue() const
113 {
114     if (m_hasExplicitValue)
115         return m_explicitValue;
116     Node* list = enclosingList(node());
117     // FIXME: This recurses to a possible depth of the length of the list.
118     // That's not good -- we need to change this to an iterative algorithm.
119     if (RenderListItem* previousItem = previousListItem(list, this))
120         return previousItem->value() + 1;
121     if (list && list->hasTagName(olTag))
122         return static_cast<HTMLOListElement*>(list)->start();
123     return 1;
124 }
125 
updateValueNow() const126 void RenderListItem::updateValueNow() const
127 {
128     m_value = calcValue();
129     m_isValueUpToDate = true;
130 }
131 
isEmpty() const132 bool RenderListItem::isEmpty() const
133 {
134     return lastChild() == m_marker;
135 }
136 
getParentOfFirstLineBox(RenderBlock * curr,RenderObject * marker)137 static RenderObject* getParentOfFirstLineBox(RenderBlock* curr, RenderObject* marker)
138 {
139     RenderObject* firstChild = curr->firstChild();
140     if (!firstChild)
141         return 0;
142 
143     for (RenderObject* currChild = firstChild; currChild; currChild = currChild->nextSibling()) {
144         if (currChild == marker)
145             continue;
146 
147         if (currChild->isInline() && (!currChild->isRenderInline() || curr->generatesLineBoxesForInlineChild(currChild)))
148             return curr;
149 
150         if (currChild->isFloating() || currChild->isPositioned())
151             continue;
152 
153         if (currChild->isTable() || !currChild->isRenderBlock())
154             break;
155 
156         if (curr->isListItem() && currChild->style()->htmlHacks() && currChild->node() &&
157             (currChild->node()->hasTagName(ulTag)|| currChild->node()->hasTagName(olTag)))
158             break;
159 
160         RenderObject* lineBox = getParentOfFirstLineBox(toRenderBlock(currChild), marker);
161         if (lineBox)
162             return lineBox;
163     }
164 
165     return 0;
166 }
167 
updateValue()168 void RenderListItem::updateValue()
169 {
170     if (!m_hasExplicitValue) {
171         m_isValueUpToDate = false;
172         if (m_marker)
173             m_marker->setNeedsLayoutAndPrefWidthsRecalc();
174     }
175 }
176 
firstNonMarkerChild(RenderObject * parent)177 static RenderObject* firstNonMarkerChild(RenderObject* parent)
178 {
179     RenderObject* result = parent->firstChild();
180     while (result && result->isListMarker())
181         result = result->nextSibling();
182     return result;
183 }
184 
updateMarkerLocation()185 void RenderListItem::updateMarkerLocation()
186 {
187     // Sanity check the location of our marker.
188     if (m_marker) {
189         RenderObject* markerPar = m_marker->parent();
190         RenderObject* lineBoxParent = getParentOfFirstLineBox(this, m_marker);
191         if (!lineBoxParent) {
192             // If the marker is currently contained inside an anonymous box,
193             // then we are the only item in that anonymous box (since no line box
194             // parent was found).  It's ok to just leave the marker where it is
195             // in this case.
196             if (markerPar && markerPar->isAnonymousBlock())
197                 lineBoxParent = markerPar;
198             else
199                 lineBoxParent = this;
200         }
201 
202         if (markerPar != lineBoxParent || m_marker->prefWidthsDirty()) {
203             // Removing and adding the marker can trigger repainting in
204             // containers other than ourselves, so we need to disable LayoutState.
205             view()->disableLayoutState();
206             updateFirstLetter();
207             m_marker->remove();
208             if (!lineBoxParent)
209                 lineBoxParent = this;
210             lineBoxParent->addChild(m_marker, firstNonMarkerChild(lineBoxParent));
211             if (m_marker->prefWidthsDirty())
212                 m_marker->calcPrefWidths();
213             view()->enableLayoutState();
214         }
215     }
216 }
217 
calcPrefWidths()218 void RenderListItem::calcPrefWidths()
219 {
220     ASSERT(prefWidthsDirty());
221 
222     updateMarkerLocation();
223 
224     RenderBlock::calcPrefWidths();
225 }
226 
layout()227 void RenderListItem::layout()
228 {
229     ASSERT(needsLayout());
230 
231     updateMarkerLocation();
232     RenderBlock::layout();
233 }
234 
positionListMarker()235 void RenderListItem::positionListMarker()
236 {
237     if (m_marker && !m_marker->isInside() && m_marker->inlineBoxWrapper()) {
238         int markerOldX = m_marker->x();
239         int yOffset = 0;
240         int xOffset = 0;
241         for (RenderBox* o = m_marker->parentBox(); o != this; o = o->parentBox()) {
242             yOffset += o->y();
243             xOffset += o->x();
244         }
245 
246         bool adjustOverflow = false;
247         int markerXPos;
248         RootInlineBox* root = m_marker->inlineBoxWrapper()->root();
249 
250         if (style()->direction() == LTR) {
251             int leftLineOffset = leftRelOffset(yOffset, leftOffset(yOffset, false), false);
252             markerXPos = leftLineOffset - xOffset - paddingLeft() - borderLeft() + m_marker->marginLeft();
253             m_marker->inlineBoxWrapper()->adjustPosition(markerXPos - markerOldX, 0);
254             if (markerXPos < root->leftOverflow()) {
255                 root->setHorizontalOverflowPositions(markerXPos, root->rightOverflow());
256                 adjustOverflow = true;
257             }
258         } else {
259             int rightLineOffset = rightRelOffset(yOffset, rightOffset(yOffset, false), false);
260             markerXPos = rightLineOffset - xOffset + paddingRight() + borderRight() + m_marker->marginLeft();
261             m_marker->inlineBoxWrapper()->adjustPosition(markerXPos - markerOldX, 0);
262             if (markerXPos + m_marker->width() > root->rightOverflow()) {
263                 root->setHorizontalOverflowPositions(root->leftOverflow(), markerXPos + m_marker->width());
264                 adjustOverflow = true;
265             }
266         }
267 
268         if (adjustOverflow) {
269             IntRect markerRect(markerXPos + xOffset, yOffset, m_marker->width(), m_marker->height());
270             RenderBox* o = m_marker;
271             do {
272                 o = o->parentBox();
273                 if (o->isRenderBlock())
274                     toRenderBlock(o)->addVisualOverflow(markerRect);
275                 markerRect.move(-o->x(), -o->y());
276             } while (o != this);
277         }
278     }
279 }
280 
paint(PaintInfo & paintInfo,int tx,int ty)281 void RenderListItem::paint(PaintInfo& paintInfo, int tx, int ty)
282 {
283     if (!height())
284         return;
285 
286     RenderBlock::paint(paintInfo, tx, ty);
287 }
288 
markerText() const289 const String& RenderListItem::markerText() const
290 {
291     if (m_marker)
292         return m_marker->text();
293     DEFINE_STATIC_LOCAL(String, staticNullString, ());
294     return staticNullString;
295 }
296 
explicitValueChanged()297 void RenderListItem::explicitValueChanged()
298 {
299     if (m_marker)
300         m_marker->setNeedsLayoutAndPrefWidthsRecalc();
301     Node* listNode = enclosingList(node());
302     RenderObject* listRenderer = 0;
303     if (listNode)
304         listRenderer = listNode->renderer();
305     for (RenderObject* renderer = this; renderer; renderer = renderer->nextInPreOrder(listRenderer))
306         if (renderer->isListItem()) {
307             RenderListItem* item = toRenderListItem(renderer);
308             if (!item->m_hasExplicitValue) {
309                 item->m_isValueUpToDate = false;
310                 if (RenderListMarker* marker = item->m_marker)
311                     marker->setNeedsLayoutAndPrefWidthsRecalc();
312             }
313         }
314 }
315 
setExplicitValue(int value)316 void RenderListItem::setExplicitValue(int value)
317 {
318     if (m_hasExplicitValue && m_explicitValue == value)
319         return;
320     m_explicitValue = value;
321     m_value = value;
322     m_hasExplicitValue = true;
323     explicitValueChanged();
324 }
325 
clearExplicitValue()326 void RenderListItem::clearExplicitValue()
327 {
328     if (!m_hasExplicitValue)
329         return;
330     m_hasExplicitValue = false;
331     m_isValueUpToDate = false;
332     explicitValueChanged();
333 }
334 
335 } // namespace WebCore
336