[LayoutReloaded] Ensure that positioning happens within the formatting context
[WebKit-https.git] / Tools / LayoutReloaded / LayoutTree / Box.js
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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 var Layout = { }
27
28 Layout.Box = class Box {
29     constructor(node, id) {
30         this.m_id = id;
31         this.m_rendererName = null;
32         this.m_node = node;
33         this.m_parent = null;
34         this.m_nextSibling = null;
35         this.m_previousSibling = null;
36         this.m_isAnonymous = false;
37         this.m_establishedFormattingContext = null;
38         this.m_displayBox = null;
39     }
40
41     id() {
42         return this.m_id;
43     }
44
45     setRendererName(name) {
46         this.m_rendererName = name;
47     }
48
49     name() {
50         return this.m_rendererName;
51     }
52
53     node() {
54         return this.m_node;
55     }
56
57     parent() {
58         return this.m_parent;
59     }
60
61     nextSibling() {
62         return this.m_nextSibling;
63     }
64
65     nextInFlowSibling() {
66         let nextInFlowSibling = this.nextSibling();
67         while (nextInFlowSibling) {
68             if (nextInFlowSibling.isInFlow())
69                 return nextInFlowSibling;
70             nextInFlowSibling = nextInFlowSibling.nextSibling();
71         }
72         return null;
73     }
74
75     previousSibling() {
76         return this.m_previousSibling;
77     }
78
79     previousInFlowSibling() {
80         let previousInFlowSibling = this.previousSibling();
81         while (previousInFlowSibling) {
82             if (previousInFlowSibling.isInFlow())
83                 return previousInFlowSibling;
84             previousInFlowSibling = previousInFlowSibling.previousSibling();
85         }
86         return null;
87     }
88
89     setParent(parent) {
90         this.m_parent = parent;
91     }
92
93     setNextSibling(nextSibling) {
94         this.m_nextSibling = nextSibling;
95     }
96     
97     setPreviousSibling(previousSibling) {
98         this.m_previousSibling = previousSibling;
99     }
100
101     setDisplayBox(displayBox) {
102         ASSERT(!this.m_displayBox);
103         this.m_displayBox = displayBox;
104     }
105
106     displayBox() {
107         ASSERT(this.m_displayBox);
108         return this.m_displayBox;
109     }
110
111     rect() {
112         return this.displayBox().rect();
113     }
114
115     topLeft() {
116         return this.rect().topLeft();
117     }
118
119     bottomRight() {
120         return this.rect().bottomRight();
121     }
122
123     setTopLeft(topLeft) {
124         this.displayBox().setTopLeft(topLeft);
125     }
126
127     setSize(size) {
128         this.displayBox().setSize(size);
129     }
130
131     setWidth(width) {
132         this.displayBox().setWidth(width);
133     }
134
135     setHeight(height) {
136         this.displayBox().setHeight(height);
137     }
138
139     isContainer() {
140         return false;
141     }
142
143     isBlockLevelBox() {
144         return Utils.isBlockLevelElement(this.node());
145     }
146
147     isBlockContainerBox() {
148         return Utils.isBlockContainerElement(this.node());
149     }
150
151     isInlineLevelBox() {
152         return Utils.isInlineLevelElement(this.node());
153     }
154
155     setIsAnonymous() {
156         this.m_isAnonymous = true;
157     }
158
159     isAnonymous() {
160         return this.m_isAnonymous;
161     }
162
163     establishesFormattingContext() {
164         if (this.isAnonymous())
165             return false;
166         return this.establishesBlockFormattingContext() || this.establishesInlineFormattingContext();
167     }
168
169     establishedFormattingContext() {
170         if (this.establishesFormattingContext() && !this.m_establishedFormattingContext)
171             this.m_establishedFormattingContext = this.establishesBlockFormattingContext() ? new BlockFormattingContext(this) : new InlineFormattingContext(this);
172         return this.m_establishedFormattingContext;
173     }
174
175     establishesBlockFormattingContext() {
176         // 9.4.1 Block formatting contexts
177         // Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions)
178         // that are not block boxes, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport)
179         // establish new block formatting contexts for their contents.
180         return this.isFloatingPositioned() || this.isAbsolutelyPositioned() || (this.isBlockContainerBox() && !this.isBlockLevelBox())
181             || (this.isBlockLevelBox() && !Utils.isOverflowVisible(this));
182     }
183
184     establishesInlineFormattingContext() {
185         return false;
186     }
187
188     isPositioned() {
189         return this.isOutOfFlowPositioned() || this.isRelativelyPositioned();
190     }
191
192     isRelativelyPositioned() {
193         return Utils.isRelativelyPositioned(this);
194     }
195
196     isAbsolutelyPositioned() {
197         return Utils.isAbsolutelyPositioned(this) || this.isFixedPositioned();
198     }
199
200     isFixedPositioned() {
201         return Utils.isFixedPositioned(this);
202     }
203
204     isInFlow() {
205         if (this.isAnonymous())
206             return true;
207         return !this.isFloatingOrOutOfFlowPositioned();
208     }
209
210     isOutOfFlowPositioned() {
211         return this.isAbsolutelyPositioned() || this.isFixedPositioned();
212     }
213
214     isInFlowPositioned() {
215         return this.isPositioned() && !this.isOutOfFlowPositioned();
216     }
217
218     isFloatingPositioned() {
219         return Utils.isFloatingPositioned(this);
220     }
221
222     isFloatingOrOutOfFlowPositioned() {
223         return this.isFloatingPositioned() || this.isOutOfFlowPositioned();
224     }
225
226     isRootBox() {
227         // FIXME: This should just be a simple instanceof check, but we are in the mainframe while the test document is in an iframe
228         // Let's just return root for both the RenderView and the <html> element.
229         return !this.parent() || !this.parent().parent();
230     }
231
232     containingBlock() {
233         if (!this.parent())
234             return null;
235         if (!this.isPositioned() || this.isInFlowPositioned())
236             return this.parent();
237         if (this.isAbsolutelyPositioned() && !this.isFixedPositioned()) {
238             let ascendant = this.parent();
239             while (ascendant.parent() && !ascendant.isPositioned())
240                 ascendant = ascendant.parent();
241             return ascendant;
242         }
243         if (this.isFixedPositioned()) {
244             let ascendant = this.parent();
245             while (ascendant.parent())
246                 ascendant = ascendant.parent();
247             return ascendant;
248         }
249         ASSERT_NOT_REACHED();
250         return null;
251     }
252
253     borderBox() {
254         return this.displayBox().borderBox();
255     }
256
257     paddingBox() {
258         return this.displayBox().paddingBox();
259     }
260
261     contentBox() {
262         return this.displayBox().contentBox();
263     }
264 }