1 | /* |
2 | * Copyright (C) 2018 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 "LayoutBox.h" |
28 | |
29 | #if ENABLE(LAYOUT_FORMATTING_CONTEXT) |
30 | |
31 | #include "LayoutContainer.h" |
32 | #include "RenderStyle.h" |
33 | #include <wtf/IsoMallocInlines.h> |
34 | |
35 | namespace WebCore { |
36 | namespace Layout { |
37 | |
38 | WTF_MAKE_ISO_ALLOCATED_IMPL(Box); |
39 | |
40 | Box::Box(Optional<ElementAttributes> attributes, RenderStyle&& style, BaseTypeFlags baseTypeFlags) |
41 | : m_style(WTFMove(style)) |
42 | , m_elementAttributes(attributes) |
43 | , m_baseTypeFlags(baseTypeFlags) |
44 | { |
45 | if (isReplaced()) |
46 | m_replaced = std::make_unique<Replaced>(*this); |
47 | } |
48 | |
49 | Box::Box(Optional<ElementAttributes> attributes, RenderStyle&& style) |
50 | : Box(attributes, WTFMove(style), BaseTypeFlag::BoxFlag) |
51 | { |
52 | } |
53 | |
54 | Box::~Box() |
55 | { |
56 | } |
57 | |
58 | bool Box::establishesFormattingContext() const |
59 | { |
60 | return establishesBlockFormattingContext() || establishesInlineFormattingContext(); |
61 | } |
62 | |
63 | bool Box::establishesBlockFormattingContext() const |
64 | { |
65 | // Initial Containing Block always creates a new (inital) block formatting context. |
66 | if (!parent()) |
67 | return true; |
68 | |
69 | // 9.4.1 Block formatting contexts |
70 | // Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) |
71 | // that are not block boxes, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport) |
72 | // establish new block formatting contexts for their contents. |
73 | if (isFloatingPositioned() || isAbsolutelyPositioned()) |
74 | return true; |
75 | |
76 | if (isBlockContainerBox() && !isBlockLevelBox()) |
77 | return true; |
78 | |
79 | if (isBlockLevelBox() && !isOverflowVisible()) |
80 | return true; |
81 | |
82 | return false; |
83 | } |
84 | |
85 | bool Box::establishesBlockFormattingContextOnly() const |
86 | { |
87 | return establishesBlockFormattingContext() && !establishesInlineFormattingContext(); |
88 | } |
89 | |
90 | bool Box::isRelativelyPositioned() const |
91 | { |
92 | return m_style.position() == PositionType::Relative; |
93 | } |
94 | |
95 | bool Box::isStickyPositioned() const |
96 | { |
97 | return m_style.position() == PositionType::Sticky; |
98 | } |
99 | |
100 | bool Box::isAbsolutelyPositioned() const |
101 | { |
102 | return m_style.position() == PositionType::Absolute || isFixedPositioned(); |
103 | } |
104 | |
105 | bool Box::isFixedPositioned() const |
106 | { |
107 | return m_style.position() == PositionType::Fixed; |
108 | } |
109 | |
110 | bool Box::isFloatingPositioned() const |
111 | { |
112 | // FIXME: Rendering code caches values like this. (style="position: absolute; float: left") |
113 | if (isOutOfFlowPositioned()) |
114 | return false; |
115 | return m_style.floating() != Float::No; |
116 | } |
117 | |
118 | bool Box::isLeftFloatingPositioned() const |
119 | { |
120 | if (!isFloatingPositioned()) |
121 | return false; |
122 | return m_style.floating() == Float::Left; |
123 | } |
124 | |
125 | bool Box::isRightFloatingPositioned() const |
126 | { |
127 | if (!isFloatingPositioned()) |
128 | return false; |
129 | return m_style.floating() == Float::Right; |
130 | } |
131 | |
132 | bool Box::hasFloatClear() const |
133 | { |
134 | return m_style.clear() != Clear::None; |
135 | } |
136 | |
137 | const Container* Box::containingBlock() const |
138 | { |
139 | // The containing block in which the root element lives is a rectangle called the initial containing block. |
140 | // For other elements, if the element's position is 'relative' or 'static', the containing block is formed by the |
141 | // content edge of the nearest block container ancestor box. |
142 | // If the element has 'position: fixed', the containing block is established by the viewport |
143 | // If the element has 'position: absolute', the containing block is established by the nearest ancestor with a |
144 | // 'position' of 'absolute', 'relative' or 'fixed'. |
145 | if (!parent()) |
146 | return nullptr; |
147 | |
148 | if (!isPositioned() || isInFlowPositioned()) { |
149 | auto* nearestBlockContainer = parent(); |
150 | for (; nearestBlockContainer->parent() && !nearestBlockContainer->isBlockContainerBox(); nearestBlockContainer = nearestBlockContainer->parent()) { } |
151 | return nearestBlockContainer; |
152 | } |
153 | |
154 | if (isFixedPositioned()) { |
155 | auto* ancestor = parent(); |
156 | for (; ancestor->parent() && !ancestor->style().hasTransform(); ancestor = ancestor->parent()) { } |
157 | return ancestor; |
158 | } |
159 | |
160 | if (isOutOfFlowPositioned()) { |
161 | auto* ancestor = parent(); |
162 | for (; ancestor->parent() && !ancestor->isPositioned() && !ancestor->style().hasTransform(); ancestor = ancestor->parent()) { } |
163 | return ancestor; |
164 | } |
165 | |
166 | ASSERT_NOT_REACHED(); |
167 | return nullptr; |
168 | } |
169 | |
170 | const Container& Box::formattingContextRoot() const |
171 | { |
172 | for (auto* ancestor = containingBlock(); ancestor; ancestor = ancestor->containingBlock()) { |
173 | if (ancestor->establishesFormattingContext()) |
174 | return *ancestor; |
175 | } |
176 | |
177 | // Initial containing block always establishes a formatting context. |
178 | if (isInitialContainingBlock()) |
179 | return downcast<Container>(*this); |
180 | |
181 | RELEASE_ASSERT_NOT_REACHED(); |
182 | } |
183 | |
184 | const Container& Box::initialContainingBlock() const |
185 | { |
186 | if (isInitialContainingBlock()) |
187 | return downcast<Container>(*this); |
188 | |
189 | auto* parent = this->parent(); |
190 | for (; parent->parent(); parent = parent->parent()) { } |
191 | |
192 | return *parent; |
193 | } |
194 | |
195 | bool Box::isDescendantOf(const Container& container) const |
196 | { |
197 | for (auto* ancestor = containingBlock(); ancestor; ancestor = ancestor->containingBlock()) { |
198 | if (ancestor == &container) |
199 | return true; |
200 | } |
201 | return false; |
202 | } |
203 | |
204 | bool Box::isInlineBlockBox() const |
205 | { |
206 | return m_style.display() == DisplayType::InlineBlock; |
207 | } |
208 | |
209 | bool Box::isBlockLevelBox() const |
210 | { |
211 | // Block level elements generate block level boxes. |
212 | auto display = m_style.display(); |
213 | return display == DisplayType::Block || display == DisplayType::ListItem || display == DisplayType::Table; |
214 | } |
215 | |
216 | bool Box::isInlineLevelBox() const |
217 | { |
218 | // Inline level elements generate inline level boxes. |
219 | auto display = m_style.display(); |
220 | return display == DisplayType::Inline || display == DisplayType::InlineBlock || display == DisplayType::InlineTable; |
221 | } |
222 | |
223 | bool Box::isBlockContainerBox() const |
224 | { |
225 | // Inline level elements generate inline level boxes. |
226 | auto display = m_style.display(); |
227 | return display == DisplayType::Block || display == DisplayType::ListItem || display == DisplayType::InlineBlock || display == DisplayType::TableCell || display == DisplayType::TableCaption; // TODO && !replaced element |
228 | } |
229 | |
230 | bool Box::isInitialContainingBlock() const |
231 | { |
232 | return !parent(); |
233 | } |
234 | |
235 | const Box* Box::nextInFlowSibling() const |
236 | { |
237 | auto* nextSibling = this->nextSibling(); |
238 | while (nextSibling && !nextSibling->isInFlow()) |
239 | nextSibling = nextSibling->nextSibling(); |
240 | return nextSibling; |
241 | } |
242 | |
243 | const Box* Box::nextInFlowOrFloatingSibling() const |
244 | { |
245 | auto* nextSibling = this->nextSibling(); |
246 | while (nextSibling && !(nextSibling->isInFlow() || nextSibling->isFloatingPositioned())) |
247 | nextSibling = nextSibling->nextSibling(); |
248 | return nextSibling; |
249 | } |
250 | |
251 | const Box* Box::previousInFlowSibling() const |
252 | { |
253 | auto* previousSibling = this->previousSibling(); |
254 | while (previousSibling && !previousSibling->isInFlow()) |
255 | previousSibling = previousSibling->previousSibling(); |
256 | return previousSibling; |
257 | } |
258 | |
259 | const Box* Box::previousInFlowOrFloatingSibling() const |
260 | { |
261 | auto* previousSibling = this->previousSibling(); |
262 | while (previousSibling && !(previousSibling->isInFlow() || previousSibling->isFloatingPositioned())) |
263 | previousSibling = previousSibling->previousSibling(); |
264 | return previousSibling; |
265 | } |
266 | |
267 | bool Box::isOverflowVisible() const |
268 | { |
269 | auto isOverflowVisible = m_style.overflowX() == Overflow::Visible || m_style.overflowY() == Overflow::Visible; |
270 | // UAs must apply the 'overflow' property set on the root element to the viewport. When the root element is an HTML "HTML" element |
271 | // or an XHTML "html" element, and that element has an HTML "BODY" element or an XHTML "body" element as a child, |
272 | // user agents must instead apply the 'overflow' property from the first such child element to the viewport, |
273 | // if the value on the root element is 'visible'. The 'visible' value when used for the viewport must be interpreted as 'auto'. |
274 | // The element from which the value is propagated must have a used value for 'overflow' of 'visible'. |
275 | if (isBodyBox()) { |
276 | auto* documentBox = parent(); |
277 | ASSERT(documentBox); |
278 | if (!documentBox->isDocumentBox()) |
279 | return isOverflowVisible; |
280 | if (!documentBox->isOverflowVisible()) |
281 | return isOverflowVisible; |
282 | return true; |
283 | } |
284 | if (isInitialContainingBlock()) { |
285 | auto* documentBox = downcast<Container>(*this).firstChild(); |
286 | if (!documentBox || !documentBox->isDocumentBox() || !is<Container>(documentBox)) |
287 | return isOverflowVisible; |
288 | auto* bodyBox = downcast<Container>(documentBox)->firstChild(); |
289 | if (!bodyBox || !bodyBox->isBodyBox()) |
290 | return isOverflowVisible; |
291 | auto& bodyBoxStyle = bodyBox->style(); |
292 | return bodyBoxStyle.overflowX() == Overflow::Visible || bodyBoxStyle.overflowY() == Overflow::Visible; |
293 | } |
294 | return isOverflowVisible; |
295 | } |
296 | |
297 | bool Box::isPaddingApplicable() const |
298 | { |
299 | // 8.4 Padding properties: |
300 | // Applies to: all elements except table-row-group, table-header-group, table-footer-group, table-row, table-column-group and table-column |
301 | if (isAnonymous()) |
302 | return false; |
303 | |
304 | auto elementType = m_elementAttributes.value().elementType; |
305 | return elementType != ElementType::TableRowGroup |
306 | && elementType != ElementType::TableHeaderGroup |
307 | && elementType != ElementType::TableFooterGroup |
308 | && elementType != ElementType::TableRow |
309 | && elementType != ElementType::TableColumnGroup |
310 | && elementType != ElementType::TableColumn; |
311 | } |
312 | |
313 | } |
314 | } |
315 | |
316 | #endif |
317 | |