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
35namespace WebCore {
36namespace Layout {
37
38WTF_MAKE_ISO_ALLOCATED_IMPL(Box);
39
40Box::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
49Box::Box(Optional<ElementAttributes> attributes, RenderStyle&& style)
50 : Box(attributes, WTFMove(style), BaseTypeFlag::BoxFlag)
51{
52}
53
54Box::~Box()
55{
56}
57
58bool Box::establishesFormattingContext() const
59{
60 return establishesBlockFormattingContext() || establishesInlineFormattingContext();
61}
62
63bool 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
85bool Box::establishesBlockFormattingContextOnly() const
86{
87 return establishesBlockFormattingContext() && !establishesInlineFormattingContext();
88}
89
90bool Box::isRelativelyPositioned() const
91{
92 return m_style.position() == PositionType::Relative;
93}
94
95bool Box::isStickyPositioned() const
96{
97 return m_style.position() == PositionType::Sticky;
98}
99
100bool Box::isAbsolutelyPositioned() const
101{
102 return m_style.position() == PositionType::Absolute || isFixedPositioned();
103}
104
105bool Box::isFixedPositioned() const
106{
107 return m_style.position() == PositionType::Fixed;
108}
109
110bool 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
118bool Box::isLeftFloatingPositioned() const
119{
120 if (!isFloatingPositioned())
121 return false;
122 return m_style.floating() == Float::Left;
123}
124
125bool Box::isRightFloatingPositioned() const
126{
127 if (!isFloatingPositioned())
128 return false;
129 return m_style.floating() == Float::Right;
130}
131
132bool Box::hasFloatClear() const
133{
134 return m_style.clear() != Clear::None;
135}
136
137const 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
170const 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
184const 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
195bool 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
204bool Box::isInlineBlockBox() const
205{
206 return m_style.display() == DisplayType::InlineBlock;
207}
208
209bool 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
216bool 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
223bool 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
230bool Box::isInitialContainingBlock() const
231{
232 return !parent();
233}
234
235const Box* Box::nextInFlowSibling() const
236{
237 auto* nextSibling = this->nextSibling();
238 while (nextSibling && !nextSibling->isInFlow())
239 nextSibling = nextSibling->nextSibling();
240 return nextSibling;
241}
242
243const 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
251const Box* Box::previousInFlowSibling() const
252{
253 auto* previousSibling = this->previousSibling();
254 while (previousSibling && !previousSibling->isInFlow())
255 previousSibling = previousSibling->previousSibling();
256 return previousSibling;
257}
258
259const 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
267bool 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
297bool 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