Fix the issue that Web Audio test case fails on PR3.
[framework/web/webkit-efl.git] / Source / WebCore / rendering / RenderListItem.cpp
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 #include <wtf/text/StringBuilder.h>
34
35 using namespace std;
36
37 namespace WebCore {
38
39 using namespace HTMLNames;
40
41 RenderListItem::RenderListItem(Node* node)
42     : RenderBlock(node)
43     , m_marker(0)
44     , m_hasExplicitValue(false)
45     , m_isValueUpToDate(false)
46     , m_notInList(false)
47 {
48     setInline(false);
49 }
50
51 void RenderListItem::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
52 {
53     RenderBlock::styleDidChange(diff, oldStyle);
54
55     if (style()->listStyleType() != NoneListStyle
56         || (style()->listStyleImage() && !style()->listStyleImage()->errorOccurred())) {
57         RefPtr<RenderStyle> newStyle = RenderStyle::create();
58         // The marker always inherits from the list item, regardless of where it might end
59         // up (e.g., in some deeply nested line box). See CSS3 spec.
60         newStyle->inheritFrom(style()); 
61         if (!m_marker)
62             m_marker = new (renderArena()) RenderListMarker(this);
63         m_marker->setStyle(newStyle.release());
64     } else if (m_marker) {
65         m_marker->destroy();
66         m_marker = 0;
67     }
68 }
69
70 void RenderListItem::willBeDestroyed()
71 {    
72     if (m_marker) {
73         m_marker->destroy();
74         m_marker = 0;
75     }
76     RenderBlock::willBeDestroyed();
77 }
78
79 static bool isList(Node* node)
80 {
81     return (node->hasTagName(ulTag) || node->hasTagName(olTag));
82 }
83
84 static Node* enclosingList(const RenderListItem* listItem)
85 {
86     Node* firstNode = 0;
87
88     for (const RenderObject* renderer = listItem->parent(); renderer; renderer = renderer->parent()) {
89         Node* node = renderer->node();
90         if (node) {
91             if (isList(node))
92                 return node;
93             if (!firstNode)
94                 firstNode = node;
95         }
96     }
97
98     // If there's no actual <ul> or <ol> list element, then the first found
99     // node acts as our list for purposes of determining what other list items
100     // should be numbered as part of the same list.
101     return firstNode;
102 }
103
104 RenderListItem* RenderListItem::nextListItem(RenderObject* list, const RenderListItem* item)
105 {
106     if (!list)
107         return 0;
108
109     RenderObject* renderer = item ? item->nextInPreOrder(list) : list->nextInPreOrder(list);
110     while (renderer) {
111         if (renderer->node() && isList(renderer->node())) {
112             // We've found a nested, independent list: nothing to do here.
113             renderer = renderer->nextInPreOrderAfterChildren(list);
114             continue;
115         }
116
117         if (renderer->isListItem())
118             return toRenderListItem(renderer);
119
120         renderer = renderer->nextInPreOrder(list);
121     }
122     return 0;
123 }
124
125 static RenderListItem* previousListItem(RenderObject* list, const RenderListItem* item)
126 {
127     for (RenderObject* renderer = item->previousInPreOrder(); renderer && renderer != list; renderer = renderer->previousInPreOrder()) {
128         if (!renderer->isListItem())
129             continue;
130         Node* otherList = enclosingList(toRenderListItem(renderer));
131         // This item is part of our current list, so it's what we're looking for.
132         if (list->node() == otherList)
133             return toRenderListItem(renderer);
134         // We found ourself inside another list; lets skip the rest of it.
135         // Use nextInPreOrder() here because the other list itself may actually
136         // be a list item itself. We need to examine it, so we do this to counteract
137         // the previousInPreOrder() that will be done by the loop.
138         if (otherList)
139             renderer = otherList->renderer()->nextInPreOrder();
140     }
141     return 0;
142 }
143
144 inline int RenderListItem::calcValue() const
145 {
146     if (m_hasExplicitValue)
147         return m_explicitValue;
148
149     Node* list = enclosingList(this);
150     RenderObject* listRenderer = list ? list->renderer() : 0;
151     HTMLOListElement* oListElement = (list && list->hasTagName(olTag)) ? static_cast<HTMLOListElement*>(list) : 0;
152     int valueStep = 1;
153     if (oListElement && oListElement->isReversed())
154         valueStep = -1;
155
156     // FIXME: This recurses to a possible depth of the length of the list.
157     // That's not good -- we need to change this to an iterative algorithm.
158     if (RenderListItem* previousItem = previousListItem(listRenderer, this))
159         return previousItem->value() + valueStep;
160
161     if (oListElement)
162         return oListElement->start();
163
164     return 1;
165 }
166
167 void RenderListItem::updateValueNow() const
168 {
169     m_value = calcValue();
170     m_isValueUpToDate = true;
171 }
172
173 bool RenderListItem::isEmpty() const
174 {
175     return lastChild() == m_marker;
176 }
177
178 static RenderObject* getParentOfFirstLineBox(RenderBlock* curr, RenderObject* marker)
179 {
180     RenderObject* firstChild = curr->firstChild();
181     if (!firstChild)
182         return 0;
183
184     bool inQuirksMode = curr->document()->inQuirksMode();
185     for (RenderObject* currChild = firstChild; currChild; currChild = currChild->nextSibling()) {
186         if (currChild == marker)
187             continue;
188
189         if (currChild->isInline() && (!currChild->isRenderInline() || curr->generatesLineBoxesForInlineChild(currChild)))
190             return curr;
191
192         if (currChild->isFloating() || currChild->isOutOfFlowPositioned())
193             continue;
194
195         if (currChild->isTable() || !currChild->isRenderBlock() || (currChild->isBox() && toRenderBox(currChild)->isWritingModeRoot()))
196             break;
197
198         if (curr->isListItem() && inQuirksMode && currChild->node() &&
199             (currChild->node()->hasTagName(ulTag)|| currChild->node()->hasTagName(olTag)))
200             break;
201
202         RenderObject* lineBox = getParentOfFirstLineBox(toRenderBlock(currChild), marker);
203         if (lineBox)
204             return lineBox;
205     }
206
207     return 0;
208 }
209
210 void RenderListItem::updateValue()
211 {
212     if (!m_hasExplicitValue) {
213         m_isValueUpToDate = false;
214         if (m_marker)
215             m_marker->setNeedsLayoutAndPrefWidthsRecalc();
216     }
217 }
218
219 static RenderObject* firstNonMarkerChild(RenderObject* parent)
220 {
221     RenderObject* result = parent->firstChild();
222     while (result && result->isListMarker())
223         result = result->nextSibling();
224     return result;
225 }
226
227 void RenderListItem::updateMarkerLocation()
228 {
229     // Sanity check the location of our marker.
230     if (m_marker) {
231         RenderObject* markerPar = m_marker->parent();
232         RenderObject* lineBoxParent = getParentOfFirstLineBox(this, m_marker);
233         if (!lineBoxParent) {
234             // If the marker is currently contained inside an anonymous box,
235             // then we are the only item in that anonymous box (since no line box
236             // parent was found).  It's ok to just leave the marker where it is
237             // in this case.
238             if (markerPar && markerPar->isAnonymousBlock())
239                 lineBoxParent = markerPar;
240             else
241                 lineBoxParent = this;
242         }
243
244         if (markerPar != lineBoxParent || m_marker->preferredLogicalWidthsDirty()) {
245             // Removing and adding the marker can trigger repainting in
246             // containers other than ourselves, so we need to disable LayoutState.
247             LayoutStateDisabler layoutStateDisabler(view());
248             updateFirstLetter();
249             m_marker->remove();
250             if (!lineBoxParent)
251                 lineBoxParent = this;
252             lineBoxParent->addChild(m_marker, firstNonMarkerChild(lineBoxParent));
253             if (m_marker->preferredLogicalWidthsDirty())
254                 m_marker->computePreferredLogicalWidths();
255             // If markerPar is an anonymous block that has lost all its children, destroy it.
256             // Extraneous anonymous blocks can cause problems for RenderBlock::updateBeforeAfterContent.
257             if (markerPar && markerPar->isAnonymousBlock() && !markerPar->firstChild() && !toRenderBlock(markerPar)->continuation())
258                 markerPar->destroy();
259         }
260     }
261 }
262
263 void RenderListItem::computePreferredLogicalWidths()
264 {
265     ASSERT(preferredLogicalWidthsDirty());
266     
267     updateMarkerLocation();
268
269     RenderBlock::computePreferredLogicalWidths();
270 }
271
272 void RenderListItem::layout()
273 {
274     ASSERT(needsLayout()); 
275
276     updateMarkerLocation();    
277     RenderBlock::layout();
278 }
279
280 void RenderListItem::addOverflowFromChildren()
281 {
282     RenderBlock::addOverflowFromChildren();
283     positionListMarker();
284 }
285
286 void RenderListItem::positionListMarker()
287 {
288     if (m_marker && m_marker->parent()->isBox() && !m_marker->isInside() && m_marker->inlineBoxWrapper()) {
289         LayoutUnit markerOldLogicalLeft = m_marker->logicalLeft();
290         LayoutUnit blockOffset = 0;
291         LayoutUnit lineOffset = 0;
292         for (RenderBox* o = m_marker->parentBox(); o != this; o = o->parentBox()) {
293             blockOffset += o->logicalTop();
294             lineOffset += o->logicalLeft();
295         }
296
297         bool adjustOverflow = false;
298         LayoutUnit markerLogicalLeft;
299         RootInlineBox* root = m_marker->inlineBoxWrapper()->root();
300         bool hitSelfPaintingLayer = false;
301         
302         RootInlineBox* rootBox = m_marker->inlineBoxWrapper()->root();
303         LayoutUnit lineTop = rootBox->lineTop();
304         LayoutUnit lineBottom = rootBox->lineBottom();
305
306         // FIXME: Need to account for relative positioning in the layout overflow.
307         if (style()->isLeftToRightDirection()) {
308             LayoutUnit leftLineOffset = logicalLeftOffsetForLine(blockOffset, logicalLeftOffsetForLine(blockOffset, false), false);
309             markerLogicalLeft = leftLineOffset - lineOffset - paddingStart() - borderStart() + m_marker->marginStart();
310             m_marker->inlineBoxWrapper()->adjustLineDirectionPosition(markerLogicalLeft - markerOldLogicalLeft);
311             for (InlineFlowBox* box = m_marker->inlineBoxWrapper()->parent(); box; box = box->parent()) {
312                 LayoutRect newLogicalVisualOverflowRect = box->logicalVisualOverflowRect(lineTop, lineBottom);
313                 LayoutRect newLogicalLayoutOverflowRect = box->logicalLayoutOverflowRect(lineTop, lineBottom);
314                 if (markerLogicalLeft < newLogicalVisualOverflowRect.x() && !hitSelfPaintingLayer) {
315                     newLogicalVisualOverflowRect.setWidth(newLogicalVisualOverflowRect.maxX() - markerLogicalLeft);
316                     newLogicalVisualOverflowRect.setX(markerLogicalLeft);
317                     if (box == root)
318                         adjustOverflow = true;
319                 }
320                 if (markerLogicalLeft < newLogicalLayoutOverflowRect.x()) {
321                     newLogicalLayoutOverflowRect.setWidth(newLogicalLayoutOverflowRect.maxX() - markerLogicalLeft);
322                     newLogicalLayoutOverflowRect.setX(markerLogicalLeft);
323                     if (box == root)
324                         adjustOverflow = true;
325                 }
326                 box->setOverflowFromLogicalRects(newLogicalLayoutOverflowRect, newLogicalVisualOverflowRect, lineTop, lineBottom);
327                 if (box->boxModelObject()->hasSelfPaintingLayer())
328                     hitSelfPaintingLayer = true;
329             }
330         } else {
331             markerLogicalLeft = m_marker->logicalLeft() + paddingStart() + borderStart() + m_marker->marginEnd();
332             LayoutUnit rightLineOffset = logicalRightOffsetForLine(blockOffset, logicalRightOffsetForLine(blockOffset, false), false);
333             markerLogicalLeft = rightLineOffset - lineOffset + paddingStart() + borderStart() + m_marker->marginEnd();
334             m_marker->inlineBoxWrapper()->adjustLineDirectionPosition(markerLogicalLeft - markerOldLogicalLeft);
335             for (InlineFlowBox* box = m_marker->inlineBoxWrapper()->parent(); box; box = box->parent()) {
336                 LayoutRect newLogicalVisualOverflowRect = box->logicalVisualOverflowRect(lineTop, lineBottom);
337                 LayoutRect newLogicalLayoutOverflowRect = box->logicalLayoutOverflowRect(lineTop, lineBottom);
338                 if (markerLogicalLeft + m_marker->logicalWidth() > newLogicalVisualOverflowRect.maxX() && !hitSelfPaintingLayer) {
339                     newLogicalVisualOverflowRect.setWidth(markerLogicalLeft + m_marker->logicalWidth() - newLogicalVisualOverflowRect.x());
340                     if (box == root)
341                         adjustOverflow = true;
342                 }
343                 if (markerLogicalLeft + m_marker->logicalWidth() > newLogicalLayoutOverflowRect.maxX()) {
344                     newLogicalLayoutOverflowRect.setWidth(markerLogicalLeft + m_marker->logicalWidth() - newLogicalLayoutOverflowRect.x());
345                     if (box == root)
346                         adjustOverflow = true;
347                 }
348                 box->setOverflowFromLogicalRects(newLogicalLayoutOverflowRect, newLogicalVisualOverflowRect, lineTop, lineBottom);
349                 
350                 if (box->boxModelObject()->hasSelfPaintingLayer())
351                     hitSelfPaintingLayer = true;
352             }
353         }
354
355         if (adjustOverflow) {
356             LayoutRect markerRect(markerLogicalLeft + lineOffset, blockOffset, m_marker->width(), m_marker->height());
357             if (!style()->isHorizontalWritingMode())
358                 markerRect = markerRect.transposedRect();
359             RenderBox* o = m_marker;
360             bool propagateVisualOverflow = true;
361             bool propagateLayoutOverflow = true;
362             do {
363                 o = o->parentBox();
364                 if (o->hasOverflowClip())
365                     propagateVisualOverflow = false;
366                 if (o->isRenderBlock()) {
367                     if (propagateVisualOverflow)
368                         toRenderBlock(o)->addVisualOverflow(markerRect);
369                     if (propagateLayoutOverflow)
370                         toRenderBlock(o)->addLayoutOverflow(markerRect);
371                 }
372                 if (o->hasOverflowClip())
373                     propagateLayoutOverflow = false;
374                 if (o->hasSelfPaintingLayer())
375                     propagateVisualOverflow = false;
376                 markerRect.moveBy(-o->location());
377             } while (o != this && propagateVisualOverflow && propagateLayoutOverflow);
378         }
379     }
380 }
381
382 void RenderListItem::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
383 {
384     if (!logicalHeight() && hasOverflowClip())
385         return;
386
387     RenderBlock::paint(paintInfo, paintOffset);
388 }
389
390 const String& RenderListItem::markerText() const
391 {
392     if (m_marker)
393         return m_marker->text();
394     DEFINE_STATIC_LOCAL(String, staticNullString, ());
395     return staticNullString;
396 }
397
398 String RenderListItem::markerTextWithSuffix() const
399 {
400     if (!m_marker)
401         return String();
402
403     // Append the suffix for the marker in the right place depending
404     // on the direction of the text (right-to-left or left-to-right).
405
406     const String& markerText = m_marker->text();
407     const String markerSuffix = m_marker->suffix();
408     StringBuilder result;
409
410     if (!m_marker->style()->isLeftToRightDirection())
411         result.append(markerSuffix);
412
413     result.append(markerText);
414
415     if (m_marker->style()->isLeftToRightDirection())
416         result.append(markerSuffix);
417
418     return result.toString();
419 }
420
421 void RenderListItem::explicitValueChanged()
422 {
423     if (m_marker)
424         m_marker->setNeedsLayoutAndPrefWidthsRecalc();
425     Node* listNode = enclosingList(this);
426     RenderObject* listRenderer = 0;
427     if (listNode)
428         listRenderer = listNode->renderer();
429     for (RenderListItem* item = this; item; item = nextListItem(listRenderer, item))
430         item->updateValue();
431 }
432
433 void RenderListItem::setExplicitValue(int value)
434 {
435     ASSERT(node());
436
437     if (m_hasExplicitValue && m_explicitValue == value)
438         return;
439     m_explicitValue = value;
440     m_value = value;
441     m_hasExplicitValue = true;
442     explicitValueChanged();
443 }
444
445 void RenderListItem::clearExplicitValue()
446 {
447     ASSERT(node());
448
449     if (!m_hasExplicitValue)
450         return;
451     m_hasExplicitValue = false;
452     m_isValueUpToDate = false;
453     explicitValueChanged();
454 }
455
456 static RenderListItem* previousOrNextItem(bool isListReversed, RenderObject* list, RenderListItem* item)
457 {
458     return isListReversed ? previousListItem(list, item) : RenderListItem::nextListItem(list, item);
459 }
460
461 void RenderListItem::updateListMarkerNumbers()
462 {
463     Node* listNode = enclosingList(this);
464     ASSERT(listNode && listNode->renderer());
465     if (!listNode || !listNode->renderer())
466         return;
467
468     bool isListReversed = false;
469     RenderObject* list = listNode->renderer();
470     HTMLOListElement* oListElement = (listNode && listNode->hasTagName(olTag)) ? static_cast<HTMLOListElement*>(listNode) : 0;
471     if (oListElement) {
472         oListElement->itemCountChanged();
473         isListReversed = oListElement->isReversed();
474     }
475     for (RenderListItem* item = previousOrNextItem(isListReversed, list, this); item; item = previousOrNextItem(isListReversed, list, item)) {
476         if (!item->m_isValueUpToDate) {
477             // If an item has been marked for update before, we can safely
478             // assume that all the following ones have too.
479             // This gives us the opportunity to stop here and avoid
480             // marking the same nodes again.
481             break;
482         }
483         item->updateValue();
484     }
485 }
486
487 } // namespace WebCore