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