1 | /* |
2 | * Copyright (C) 2017 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "RenderTreeBuilderTable.h" |
28 | |
29 | #include "RenderTableCaption.h" |
30 | #include "RenderTableCell.h" |
31 | #include "RenderTableCol.h" |
32 | #include "RenderTableRow.h" |
33 | #include "RenderTreeBuilder.h" |
34 | |
35 | namespace WebCore { |
36 | |
37 | RenderTreeBuilder::Table::Table(RenderTreeBuilder& builder) |
38 | : m_builder(builder) |
39 | { |
40 | } |
41 | |
42 | RenderElement& RenderTreeBuilder::Table::findOrCreateParentForChild(RenderTableRow& parent, const RenderObject& child, RenderObject*& beforeChild) |
43 | { |
44 | if (is<RenderTableCell>(child)) |
45 | return parent; |
46 | |
47 | if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == &parent) { |
48 | auto* previousSibling = beforeChild->previousSibling(); |
49 | if (is<RenderTableCell>(previousSibling) && previousSibling->isAnonymous()) { |
50 | beforeChild = nullptr; |
51 | return downcast<RenderElement>(*previousSibling); |
52 | } |
53 | } |
54 | |
55 | auto* lastChild = beforeChild ? beforeChild : parent.lastCell(); |
56 | if (lastChild) { |
57 | if (is<RenderTableCell>(*lastChild) && lastChild->isAnonymous() && !lastChild->isBeforeOrAfterContent()) { |
58 | if (beforeChild == lastChild) |
59 | beforeChild = downcast<RenderElement>(*lastChild).firstChild(); |
60 | return downcast<RenderElement>(*lastChild); |
61 | } |
62 | |
63 | // Try to find an anonymous container for the child. |
64 | if (auto* lastChildParent = lastChild->parent()) { |
65 | if (lastChildParent->isAnonymous() && !lastChildParent->isBeforeOrAfterContent()) { |
66 | // If beforeChild is inside an anonymous cell, insert into the cell. |
67 | if (!is<RenderTableCell>(*lastChild)) |
68 | return *lastChildParent; |
69 | // If beforeChild is inside an anonymous row, insert into the row. |
70 | if (is<RenderTableRow>(*lastChildParent)) { |
71 | auto newCell = RenderTableCell::createAnonymousWithParentRenderer(parent); |
72 | auto& cell = *newCell; |
73 | m_builder.attach(*lastChildParent, WTFMove(newCell), beforeChild); |
74 | beforeChild = nullptr; |
75 | return cell; |
76 | } |
77 | } |
78 | } |
79 | } |
80 | auto newCell = RenderTableCell::createAnonymousWithParentRenderer(parent); |
81 | auto& cell = *newCell; |
82 | m_builder.attach(parent, WTFMove(newCell), beforeChild); |
83 | beforeChild = nullptr; |
84 | return cell; |
85 | } |
86 | |
87 | RenderElement& RenderTreeBuilder::Table::findOrCreateParentForChild(RenderTableSection& parent, const RenderObject& child, RenderObject*& beforeChild) |
88 | { |
89 | if (is<RenderTableRow>(child)) |
90 | return parent; |
91 | |
92 | auto* lastChild = beforeChild ? beforeChild : parent.lastRow(); |
93 | if (is<RenderTableRow>(lastChild) && lastChild->isAnonymous() && !lastChild->isBeforeOrAfterContent()) { |
94 | if (beforeChild == lastChild) |
95 | beforeChild = downcast<RenderTableRow>(*lastChild).firstCell(); |
96 | return downcast<RenderElement>(*lastChild); |
97 | } |
98 | |
99 | if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == &parent) { |
100 | auto* row = beforeChild->previousSibling(); |
101 | if (is<RenderTableRow>(row) && row->isAnonymous()) { |
102 | beforeChild = nullptr; |
103 | return downcast<RenderElement>(*row); |
104 | } |
105 | } |
106 | |
107 | // If beforeChild is inside an anonymous cell/row, insert into the cell or into |
108 | // the anonymous row containing it, if there is one. |
109 | auto* parentCandidate = lastChild; |
110 | while (parentCandidate && parentCandidate->parent()->isAnonymous() && !is<RenderTableRow>(*parentCandidate)) |
111 | parentCandidate = parentCandidate->parent(); |
112 | if (is<RenderTableRow>(parentCandidate) && parentCandidate->isAnonymous() && !parentCandidate->isBeforeOrAfterContent()) |
113 | return downcast<RenderElement>(*parentCandidate); |
114 | |
115 | auto newRow = RenderTableRow::createAnonymousWithParentRenderer(parent); |
116 | auto& row = *newRow; |
117 | m_builder.attach(parent, WTFMove(newRow), beforeChild); |
118 | beforeChild = nullptr; |
119 | return row; |
120 | } |
121 | |
122 | RenderElement& RenderTreeBuilder::Table::findOrCreateParentForChild(RenderTable& parent, const RenderObject& child, RenderObject*& beforeChild) |
123 | { |
124 | if (is<RenderTableCaption>(child) || is<RenderTableCol>(child) || is<RenderTableSection>(child)) |
125 | return parent; |
126 | |
127 | auto* lastChild = parent.lastChild(); |
128 | if (!beforeChild && is<RenderTableSection>(lastChild) && lastChild->isAnonymous() && !lastChild->isBeforeContent()) |
129 | return downcast<RenderElement>(*lastChild); |
130 | |
131 | if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == &parent) { |
132 | auto* section = beforeChild->previousSibling(); |
133 | if (is<RenderTableSection>(section) && section->isAnonymous()) { |
134 | beforeChild = nullptr; |
135 | return downcast<RenderElement>(*section); |
136 | } |
137 | } |
138 | |
139 | auto* parentCandidate = beforeChild; |
140 | while (parentCandidate && parentCandidate->parent()->isAnonymous() |
141 | && !is<RenderTableSection>(*parentCandidate) |
142 | && parentCandidate->style().display() != DisplayType::TableCaption |
143 | && parentCandidate->style().display() != DisplayType::TableColumnGroup) |
144 | parentCandidate = parentCandidate->parent(); |
145 | |
146 | if (parentCandidate && is<RenderTableSection>(*parentCandidate) && parentCandidate->isAnonymous() && !parent.isAfterContent(parentCandidate)) { |
147 | if (beforeChild == parentCandidate) |
148 | beforeChild = downcast<RenderTableSection>(*parentCandidate).firstRow(); |
149 | return downcast<RenderElement>(*parentCandidate); |
150 | } |
151 | |
152 | if (beforeChild && !is<RenderTableSection>(*beforeChild) |
153 | && beforeChild->style().display() != DisplayType::TableCaption |
154 | && beforeChild->style().display() != DisplayType::TableColumnGroup) |
155 | beforeChild = nullptr; |
156 | |
157 | auto newSection = RenderTableSection::createAnonymousWithParentRenderer(parent); |
158 | auto& section = *newSection; |
159 | m_builder.attach(parent, WTFMove(newSection), beforeChild); |
160 | beforeChild = nullptr; |
161 | return section; |
162 | } |
163 | |
164 | void RenderTreeBuilder::Table::attach(RenderTableRow& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild) |
165 | { |
166 | if (beforeChild && beforeChild->parent() != &parent) |
167 | beforeChild = m_builder.splitAnonymousBoxesAroundChild(parent, *beforeChild); |
168 | |
169 | auto& newChild = *child.get(); |
170 | ASSERT(!beforeChild || is<RenderTableCell>(*beforeChild)); |
171 | m_builder.attachToRenderElement(parent, WTFMove(child), beforeChild); |
172 | // FIXME: child should always be a RenderTableCell at this point. |
173 | if (is<RenderTableCell>(newChild)) |
174 | parent.didInsertTableCell(downcast<RenderTableCell>(newChild), beforeChild); |
175 | } |
176 | |
177 | void RenderTreeBuilder::Table::attach(RenderTableSection& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild) |
178 | { |
179 | if (beforeChild && beforeChild->parent() != &parent) |
180 | beforeChild = m_builder.splitAnonymousBoxesAroundChild(parent, *beforeChild); |
181 | |
182 | // FIXME: child should always be a RenderTableRow at this point. |
183 | if (is<RenderTableRow>(*child.get())) |
184 | parent.willInsertTableRow(downcast<RenderTableRow>(*child.get()), beforeChild); |
185 | ASSERT(!beforeChild || is<RenderTableRow>(*beforeChild)); |
186 | m_builder.attachToRenderElement(parent, WTFMove(child), beforeChild); |
187 | } |
188 | |
189 | void RenderTreeBuilder::Table::attach(RenderTable& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild) |
190 | { |
191 | if (beforeChild && beforeChild->parent() != &parent) |
192 | beforeChild = m_builder.splitAnonymousBoxesAroundChild(parent, *beforeChild); |
193 | |
194 | auto& newChild = *child.get(); |
195 | if (is<RenderTableSection>(newChild)) |
196 | parent.willInsertTableSection(downcast<RenderTableSection>(newChild), beforeChild); |
197 | else if (is<RenderTableCol>(newChild)) |
198 | parent.willInsertTableColumn(downcast<RenderTableCol>(newChild), beforeChild); |
199 | |
200 | m_builder.attachToRenderElement(parent, WTFMove(child), beforeChild); |
201 | } |
202 | |
203 | bool RenderTreeBuilder::Table::childRequiresTable(const RenderElement& parent, const RenderObject& child) |
204 | { |
205 | if (is<RenderTableCol>(child)) { |
206 | const RenderTableCol& newTableColumn = downcast<RenderTableCol>(child); |
207 | bool isColumnInColumnGroup = newTableColumn.isTableColumn() && is<RenderTableCol>(parent); |
208 | return !is<RenderTable>(parent) && !isColumnInColumnGroup; |
209 | } |
210 | if (is<RenderTableCaption>(child)) |
211 | return !is<RenderTable>(parent); |
212 | |
213 | if (is<RenderTableSection>(child)) |
214 | return !is<RenderTable>(parent); |
215 | |
216 | if (is<RenderTableRow>(child)) |
217 | return !is<RenderTableSection>(parent); |
218 | |
219 | if (is<RenderTableCell>(child)) |
220 | return !is<RenderTableRow>(parent); |
221 | |
222 | return false; |
223 | } |
224 | |
225 | void RenderTreeBuilder::Table::collapseAndDestroyAnonymousSiblingRows(RenderTableRow& row) |
226 | { |
227 | auto* section = row.section(); |
228 | if (!section) |
229 | return; |
230 | |
231 | // All siblings generated? |
232 | for (auto* current = section->firstRow(); current; current = current->nextRow()) { |
233 | if (current == &row) |
234 | continue; |
235 | if (!current->isAnonymous()) |
236 | return; |
237 | } |
238 | |
239 | RenderTableRow* rowToInsertInto = nullptr; |
240 | auto* currentRow = section->firstRow(); |
241 | while (currentRow) { |
242 | if (currentRow == &row) { |
243 | currentRow = currentRow->nextRow(); |
244 | continue; |
245 | } |
246 | if (!rowToInsertInto) { |
247 | rowToInsertInto = currentRow; |
248 | currentRow = currentRow->nextRow(); |
249 | continue; |
250 | } |
251 | m_builder.moveAllChildren(*currentRow, *rowToInsertInto, RenderTreeBuilder::NormalizeAfterInsertion::No); |
252 | auto toDestroy = m_builder.detach(*section, *currentRow); |
253 | currentRow = currentRow->nextRow(); |
254 | } |
255 | if (rowToInsertInto) |
256 | rowToInsertInto->setNeedsLayout(); |
257 | } |
258 | |
259 | } |
260 | |