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