1/**
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * Copyright (C) 2003-2006, 2010, 2017 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 "RenderTreeBuilderList.h"
26
27#include "RenderChildIterator.h"
28#include "RenderListMarker.h"
29#include "RenderMultiColumnFlow.h"
30#include "RenderRuby.h"
31#include "RenderTable.h"
32
33namespace WebCore {
34
35static RenderBlock* getParentOfFirstLineBox(RenderBlock& current, RenderObject& marker)
36{
37 bool inQuirksMode = current.document().inQuirksMode();
38 for (auto& child : childrenOfType<RenderObject>(current)) {
39 if (&child == &marker)
40 continue;
41
42 if (child.isInline() && (!is<RenderInline>(child) || current.generatesLineBoxesForInlineChild(&child)))
43 return &current;
44
45 if (child.isFloating() || child.isOutOfFlowPositioned())
46 continue;
47
48 if (!is<RenderBlock>(child) || is<RenderTable>(child) || is<RenderRubyAsBlock>(child))
49 break;
50
51 if (is<RenderBox>(child) && downcast<RenderBox>(child).isWritingModeRoot())
52 break;
53
54 if (is<RenderListItem>(current) && inQuirksMode && child.node() && isHTMLListElement(*child.node()))
55 break;
56
57 if (RenderBlock* lineBox = getParentOfFirstLineBox(downcast<RenderBlock>(child), marker))
58 return lineBox;
59 }
60
61 return nullptr;
62}
63
64static RenderObject* firstNonMarkerChild(RenderBlock& parent)
65{
66 RenderObject* child = parent.firstChild();
67 while (is<RenderListMarker>(child))
68 child = child->nextSibling();
69 return child;
70}
71
72RenderTreeBuilder::List::List(RenderTreeBuilder& builder)
73 : m_builder(builder)
74{
75}
76
77void RenderTreeBuilder::List::updateItemMarker(RenderListItem& listItemRenderer)
78{
79 auto& style = listItemRenderer.style();
80
81 if (style.listStyleType() == ListStyleType::None && (!style.listStyleImage() || style.listStyleImage()->errorOccurred())) {
82 if (auto* marker = listItemRenderer.markerRenderer())
83 m_builder.destroy(*marker);
84 return;
85 }
86
87 auto newStyle = listItemRenderer.computeMarkerStyle();
88 RenderPtr<RenderListMarker> newMarkerRenderer;
89 auto* markerRenderer = listItemRenderer.markerRenderer();
90 if (markerRenderer)
91 markerRenderer->setStyle(WTFMove(newStyle));
92 else {
93 newMarkerRenderer = WebCore::createRenderer<RenderListMarker>(listItemRenderer, WTFMove(newStyle));
94 newMarkerRenderer->initializeStyle();
95 markerRenderer = newMarkerRenderer.get();
96 listItemRenderer.setMarkerRenderer(*markerRenderer);
97 }
98
99 RenderElement* currentParent = markerRenderer->parent();
100 RenderBlock* newParent = getParentOfFirstLineBox(listItemRenderer, *markerRenderer);
101 if (!newParent) {
102 // If the marker is currently contained inside an anonymous box,
103 // then we are the only item in that anonymous box (since no line box
104 // parent was found). It's ok to just leave the marker where it is
105 // in this case.
106 if (currentParent && currentParent->isAnonymousBlock())
107 return;
108 if (auto* multiColumnFlow = listItemRenderer.multiColumnFlow())
109 newParent = multiColumnFlow;
110 else
111 newParent = &listItemRenderer;
112 }
113
114 if (newParent == currentParent)
115 return;
116
117 if (currentParent)
118 m_builder.attach(*newParent, m_builder.detach(*currentParent, *markerRenderer, RenderTreeBuilder::CanCollapseAnonymousBlock::No), firstNonMarkerChild(*newParent));
119 else
120 m_builder.attach(*newParent, WTFMove(newMarkerRenderer), firstNonMarkerChild(*newParent));
121
122 // If current parent is an anonymous block that has lost all its children, destroy it.
123 if (currentParent && currentParent->isAnonymousBlock() && !currentParent->firstChild() && !downcast<RenderBlock>(*currentParent).continuation())
124 m_builder.destroy(*currentParent);
125}
126
127}
128