[LFC] Update the block box/container list
[WebKit-https.git] / Source / WebCore / layout / layouttree / LayoutBox.cpp
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 "LayoutBoxGeometry.h"
32 #include "LayoutContainerBox.h"
33 #include "LayoutInitialContainingBlock.h"
34 #include "LayoutPhase.h"
35 #include "LayoutState.h"
36 #include "RenderStyle.h"
37 #include <wtf/IsoMallocInlines.h>
38
39 namespace WebCore {
40 namespace Layout {
41
42 WTF_MAKE_ISO_ALLOCATED_IMPL(Box);
43
44 Box::Box(Optional<ElementAttributes> attributes, RenderStyle&& style, OptionSet<BaseTypeFlag> baseTypeFlags)
45     : m_style(WTFMove(style))
46     , m_elementAttributes(attributes)
47     , m_baseTypeFlags(baseTypeFlags.toRaw())
48     , m_hasRareData(false)
49     , m_isAnonymous(false)
50 {
51 }
52
53 Box::~Box()
54 {
55     if (UNLIKELY(m_hasRareData))
56         removeRareData();
57 }
58
59 void Box::updateStyle(const RenderStyle& newStyle)
60 {
61     m_style = RenderStyle::clone(newStyle);
62 }
63
64 bool Box::establishesFormattingContext() const
65 {
66     // We need the final tree structure to tell whether a box establishes a certain formatting context. 
67     ASSERT(!Phase::isInTreeBuilding());
68     return establishesBlockFormattingContext()
69         || establishesInlineFormattingContext()
70         || establishesTableFormattingContext()
71         || establishesFlexFormattingContext()
72         || establishesIndependentFormattingContext();
73 }
74
75 bool Box::establishesBlockFormattingContext() const
76 {
77     // ICB always creates a new (inital) block formatting context.
78     if (is<InitialContainingBlock>(*this))
79         return true;
80
81     if (isTableWrapperBox())
82         return true;
83
84     // A block box that establishes an independent formatting context establishes a new block formatting context for its contents.
85     if (isBlockBox() && establishesIndependentFormattingContext())
86         return true;
87
88     // 9.4.1 Block formatting contexts
89     // Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions)
90     // that are not block boxes, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport)
91     // establish new block formatting contexts for their contents.
92     if (isFloatingPositioned()) {
93         // Not all floating or out-of-positioned block level boxes establish BFC.
94         // See [9.7 Relationships between 'display', 'position', and 'float'] for details.
95         return style().display() == DisplayType::Block;
96     }
97
98     if (isBlockContainer() && !isBlockBox())
99         return true;
100
101     if (isBlockBox() && !isOverflowVisible())
102         return true;
103
104     return false;
105 }
106
107 bool Box::establishesInlineFormattingContext() const
108 {
109     // 9.4.2 Inline formatting contexts
110     // An inline formatting context is established by a block container box that contains no block-level boxes.
111     if (!isBlockContainer())
112         return false;
113
114     if (!isContainerBox())
115         return false;
116
117     // FIXME ???
118     if (!downcast<ContainerBox>(*this).firstInFlowChild())
119         return false;
120
121     // It's enough to check the first in-flow child since we can't have both block and inline level sibling boxes.
122     return downcast<ContainerBox>(*this).firstInFlowChild()->isInlineLevelBox();
123 }
124
125 bool Box::establishesTableFormattingContext() const
126 {
127     return isTableBox();
128 }
129
130 bool Box::establishesFlexFormattingContext() const
131 {
132     return isFlexBox();
133 }
134
135 bool Box::establishesIndependentFormattingContext() const
136 {
137     // FIXME: This is where we would check for 'contain' property.
138     return isAbsolutelyPositioned() || isFlexItem();
139 }
140
141 bool Box::isRelativelyPositioned() const
142 {
143     return m_style.position() == PositionType::Relative;
144 }
145
146 bool Box::isStickyPositioned() const
147 {
148     return m_style.position() == PositionType::Sticky;
149 }
150
151 bool Box::isAbsolutelyPositioned() const
152 {
153     return m_style.position() == PositionType::Absolute || isFixedPositioned(); 
154 }
155
156 bool Box::isFixedPositioned() const
157 {
158     return m_style.position() == PositionType::Fixed;
159 }
160
161 bool Box::isFloatingPositioned() const
162 {
163     // FIXME: Rendering code caches values like this. (style="position: absolute; float: left")
164     if (isOutOfFlowPositioned())
165         return false;
166     return m_style.floating() != Float::No;
167 }
168
169 bool Box::isLeftFloatingPositioned() const
170 {
171     if (!isFloatingPositioned())
172         return false;
173     return m_style.floating() == Float::Left;
174 }
175
176 bool Box::isRightFloatingPositioned() const
177 {
178     if (!isFloatingPositioned())
179         return false;
180     return m_style.floating() == Float::Right;
181 }
182
183 bool Box::hasFloatClear() const
184 {
185     return m_style.clear() != Clear::None;
186 }
187
188 bool Box::isFloatAvoider() const
189 {
190     if (isFloatingPositioned() || hasFloatClear())
191         return true;
192
193     return establishesTableFormattingContext() || establishesIndependentFormattingContext() || establishesBlockFormattingContext();
194 }
195
196 const ContainerBox& Box::containingBlock() const
197 {
198     // Finding the containing block by traversing the tree during tree construction could provide incorrect result.
199     ASSERT(!Phase::isInTreeBuilding());
200     // If we ever end up here with the ICB, we must be doing something not-so-great.
201     RELEASE_ASSERT(!is<InitialContainingBlock>(*this));
202     // The containing block in which the root element lives is a rectangle called the initial containing block.
203     // For other elements, if the element's position is 'relative' or 'static', the containing block is formed by the
204     // content edge of the nearest block container ancestor box or which establishes a formatting context.
205     // If the element has 'position: fixed', the containing block is established by the viewport
206     // If the element has 'position: absolute', the containing block is established by the nearest ancestor with a
207     // 'position' of 'absolute', 'relative' or 'fixed'.
208     if (!isPositioned() || isInFlowPositioned()) {
209         auto* ancestor = &parent();
210         for (; !is<InitialContainingBlock>(*ancestor); ancestor = &ancestor->parent()) {
211             if (ancestor->isContainingBlockForInFlow())
212                 return *ancestor;
213         }
214         return *ancestor;
215     }
216
217     if (isFixedPositioned()) {
218         auto* ancestor = &parent();
219         for (; !is<InitialContainingBlock>(*ancestor); ancestor = &ancestor->parent()) {
220             if (ancestor->isContainingBlockForFixedPosition())
221                 return *ancestor;
222         }
223         return *ancestor;
224     }
225
226     if (isOutOfFlowPositioned()) {
227         auto* ancestor = &parent();
228         for (; !is<InitialContainingBlock>(*ancestor); ancestor = &ancestor->parent()) {
229             if (ancestor->isContainingBlockForOutOfFlowPosition())
230                 return *ancestor;
231         }
232         return *ancestor;
233     }
234
235     ASSERT_NOT_REACHED();
236     return initialContainingBlock();
237 }
238
239 const ContainerBox& Box::formattingContextRoot() const
240 {
241     // Finding the context root by traversing the tree during tree construction could provide incorrect result.
242     ASSERT(!Phase::isInTreeBuilding());
243     // We should never need to ask this question on the ICB.
244     ASSERT(!is<InitialContainingBlock>(*this));
245     // A box lives in the same formatting context as its containing block unless the containing block establishes a formatting context.
246     // However relatively positioned (inflow) inline container lives in the formatting context where its parent lives unless
247     // the parent establishes a formatting context.
248     //
249     // <div id=outer style="position: absolute"><div id=inner><span style="position: relative">content</span></div></div>
250     // While the relatively positioned inline container (span) is placed relative to its containing block "outer", it lives in the inline
251     // formatting context established by "inner".
252     auto& ancestor = isInlineLevelBox() && isInFlowPositioned() ? parent() : containingBlock();
253     if (ancestor.establishesFormattingContext())
254         return ancestor;
255     return ancestor.formattingContextRoot();
256 }
257
258 const InitialContainingBlock& Box::initialContainingBlock() const
259 {
260     if (is<InitialContainingBlock>(*this))
261         return downcast<InitialContainingBlock>(*this);
262
263     auto* ancestor = &parent();
264     for (; !is<InitialContainingBlock>(*ancestor); ancestor = &ancestor->parent()) { }
265     return downcast<InitialContainingBlock>(*ancestor);
266 }
267
268 bool Box::isInFormattingContextOf(const ContainerBox& formattingContextRoot) const
269
270     ASSERT(formattingContextRoot.establishesFormattingContext());
271     ASSERT(!is<InitialContainingBlock>(*this));
272     auto* ancestor = &containingBlock();
273     while (ancestor) {
274         if (ancestor == &formattingContextRoot)
275             return true;
276         if (is<InitialContainingBlock>(*ancestor))
277             return false;
278         ancestor = &ancestor->containingBlock();
279     }
280     ASSERT_NOT_REACHED();
281     return false;
282 }
283
284 bool Box::isInlineBlockBox() const
285 {
286     return m_style.display() == DisplayType::InlineBlock;
287 }
288
289 bool Box::isInlineTableBox() const
290 {
291     return m_style.display() == DisplayType::InlineTable;
292 }
293
294 bool Box::isBlockLevelBox() const
295 {
296     // Block level elements generate block level boxes.
297     auto display = m_style.display();
298     return display == DisplayType::Block
299         || display == DisplayType::ListItem
300         || display == DisplayType::Table
301         || display == DisplayType::Flex
302         || display == DisplayType::Grid
303         || display == DisplayType::FlowRoot;
304 }
305
306 bool Box::isBlockBox() const
307 {
308     // A block-level box that is also a block container.
309     return isBlockLevelBox() && isBlockContainer();
310 }
311
312 bool Box::isInlineLevelBox() const
313 {
314     // Inline level elements generate inline level boxes.
315     auto display = m_style.display();
316     return display == DisplayType::Inline
317         || display == DisplayType::InlineBox
318         || display == DisplayType::InlineFlex
319         || display == DisplayType::WebKitInlineFlex
320         || display == DisplayType::InlineGrid
321         || isInlineBlockBox()
322         || isInlineTableBox();
323 }
324
325 bool Box::isInlineBox() const
326 {
327     // An inline box is one that is both inline-level and whose contents participate in its containing inline formatting context.
328     // A non-replaced element with a 'display' value of 'inline' generates an inline box.
329     return m_style.display() == DisplayType::Inline && !isReplacedBox();
330 }
331
332 bool Box::isAtomicInlineLevelBox() const
333 {
334     // Inline-level boxes that are not inline boxes (such as replaced inline-level elements, inline-block elements, and inline-table elements)
335     // are called atomic inline-level boxes because they participate in their inline formatting context as a single opaque box.
336     return isInlineLevelBox() && !isInlineBox();
337 }
338
339 bool Box::isFlexItem() const
340 {
341     // Each in-flow child of a flex container becomes a flex item (https://www.w3.org/TR/css-flexbox-1/#flex-items).
342     return isInFlow() && parent().isFlexBox();
343 }
344
345 bool Box::isBlockContainer() const
346 {
347     auto display = m_style.display();
348     return display == DisplayType::Block
349         || display == DisplayType::FlowRoot
350         || display == DisplayType::ListItem
351         || isInlineBlockBox()
352         || isTableCell()
353         || isTableCaption(); // TODO && !replaced element
354 }
355
356 const Box* Box::nextInFlowSibling() const
357 {
358     auto* nextSibling = this->nextSibling();
359     while (nextSibling && !nextSibling->isInFlow())
360         nextSibling = nextSibling->nextSibling();
361     return nextSibling;
362 }
363
364 const Box* Box::nextInFlowOrFloatingSibling() const
365 {
366     auto* nextSibling = this->nextSibling();
367     while (nextSibling && !(nextSibling->isInFlow() || nextSibling->isFloatingPositioned()))
368         nextSibling = nextSibling->nextSibling();
369     return nextSibling;
370 }
371
372 const Box* Box::previousInFlowSibling() const
373 {
374     auto* previousSibling = this->previousSibling();
375     while (previousSibling && !previousSibling->isInFlow())
376         previousSibling = previousSibling->previousSibling();
377     return previousSibling;
378 }
379
380 const Box* Box::previousInFlowOrFloatingSibling() const
381 {
382     auto* previousSibling = this->previousSibling();
383     while (previousSibling && !(previousSibling->isInFlow() || previousSibling->isFloatingPositioned()))
384         previousSibling = previousSibling->previousSibling();
385     return previousSibling;
386 }
387
388 bool Box::isOverflowVisible() const
389 {
390     auto isOverflowVisible = m_style.overflowX() == Overflow::Visible || m_style.overflowY() == Overflow::Visible;
391     // UAs must apply the 'overflow' property set on the root element to the viewport. When the root element is an HTML "HTML" element
392     // or an XHTML "html" element, and that element has an HTML "BODY" element or an XHTML "body" element as a child,
393     // user agents must instead apply the 'overflow' property from the first such child element to the viewport,
394     // if the value on the root element is 'visible'. The 'visible' value when used for the viewport must be interpreted as 'auto'.
395     // The element from which the value is propagated must have a used value for 'overflow' of 'visible'.
396     if (isBodyBox()) {
397         auto& documentBox = containingBlock();
398         if (!documentBox.isDocumentBox())
399             return isOverflowVisible;
400         if (!documentBox.isOverflowVisible())
401             return isOverflowVisible;
402         return true;
403     }
404     if (is<InitialContainingBlock>(*this)) {
405         auto* documentBox = downcast<ContainerBox>(*this).firstChild();
406         if (!documentBox || !documentBox->isDocumentBox() || !is<ContainerBox>(documentBox))
407             return isOverflowVisible;
408         auto* bodyBox = downcast<ContainerBox>(documentBox)->firstChild();
409         if (!bodyBox || !bodyBox->isBodyBox())
410             return isOverflowVisible;
411         auto& bodyBoxStyle = bodyBox->style();
412         return bodyBoxStyle.overflowX() == Overflow::Visible || bodyBoxStyle.overflowY() == Overflow::Visible;
413     }
414     return isOverflowVisible;
415 }
416
417 bool Box::isPaddingApplicable() const
418 {
419     if (isAnonymous())
420         return false;
421
422     if (isTableBox() && style().borderCollapse() == BorderCollapse::Collapse) {
423         // When the table collapses its borders with inner table elements, there's no room for padding.
424         return false;
425     }
426
427     // 8.4 Padding properties:
428     // Applies to: all elements except table-row-group, table-header-group, table-footer-group, table-row, table-column-group and table-column
429     return !isTableHeader()
430         && !isTableBody()
431         && !isTableFooter()
432         && !isTableRow()
433         && !isTableColumnGroup()
434         && !isTableColumn();
435 }
436
437 void Box::setRowSpan(size_t rowSpan)
438 {
439     ensureRareData().tableCellSpan.row = rowSpan;
440 }
441
442 void Box::setColumnSpan(size_t columnSpan)
443 {
444     ensureRareData().tableCellSpan.column = columnSpan;
445 }
446
447 size_t Box::rowSpan() const
448 {
449     if (!hasRareData())
450         return 1;
451     return rareData().tableCellSpan.row;
452 }
453
454 size_t Box::columnSpan() const
455 {
456     if (!hasRareData())
457         return 1;
458     return rareData().tableCellSpan.column;
459 }
460
461 void Box::setColumnWidth(LayoutUnit columnWidth)
462 {
463     ensureRareData().columnWidth = columnWidth;
464 }
465
466 Optional<LayoutUnit> Box::columnWidth() const
467 {
468     if (!hasRareData())
469         return { };
470     return rareData().columnWidth;
471 }
472
473 void Box::setCachedGeometryForLayoutState(LayoutState& layoutState, std::unique_ptr<BoxGeometry> geometry) const
474 {
475     ASSERT(!m_cachedLayoutState);
476     m_cachedLayoutState = makeWeakPtr(layoutState);
477     m_cachedGeometryForLayoutState = WTFMove(geometry);
478 }
479
480 Box::RareDataMap& Box::rareDataMap()
481 {
482     static NeverDestroyed<RareDataMap> map;
483     return map;
484 }
485
486 const Box::BoxRareData& Box::rareData() const
487 {
488     ASSERT(hasRareData());
489     return *rareDataMap().get(this);
490 }
491
492 Box::BoxRareData& Box::ensureRareData()
493 {
494     setHasRareData(true);
495     return *rareDataMap().ensure(this, [] { return makeUnique<BoxRareData>(); }).iterator->value;
496 }
497
498 void Box::removeRareData()
499 {
500     rareDataMap().remove(this);
501     setHasRareData(false);
502 }
503
504 }
505 }
506
507 #endif