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