[LayoutReloaded] Introduce FormattingState (Block/Inline/etc)
[WebKit-https.git] / Tools / LayoutReloaded / FormattingContext / FormattingContext.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 class FormattingContext {
27     constructor(rootContainer, layoutState) {
28         this.m_rootContainer = rootContainer;
29         this.m_layoutState = layoutState;
30         this.m_floatingContext = null;
31         this.m_displayToLayout = new Map();
32         this.m_layoutToDisplay = new Map();
33         this.m_layoutStack = new Array();
34     }
35
36     rootContainer() {
37         return this.m_rootContainer;
38     }
39
40     layoutState() {
41         return this.m_layoutState;
42     }
43
44     layoutContext() {
45         return this.layoutState().layoutContext();
46     }
47
48     floatingContext() {
49         return this.m_floatingContext;
50     }
51
52     layout() {
53         ASSERT_NOT_REACHED();
54     }
55
56     computeWidth(layoutBox) {
57     }
58
59     computeHeight(layoutBox) {
60     }
61
62     marginTop(layoutBox) {
63         return Utils.computedMarginTop(layoutBox.node());
64     }
65     
66     marginLeft(layoutBox) {
67         return Utils.computedMarginLeft(layoutBox.node());
68     }
69     
70     marginBottom(layoutBox) {
71         return Utils.computedMarginBottom(layoutBox.node());
72     }
73     
74     marginRight(layoutBox) {
75         return Utils.computedMarginRight(layoutBox.node());
76     }
77
78     absoluteMarginBox(layoutBox) {
79         let displayBox = this.toDisplayBox(layoutBox);
80         let absoluteContentBox = new LayoutRect(this._toRootAbsolutePosition(layoutBox), displayBox.size());
81         absoluteContentBox.moveBy(new LayoutSize(-this.marginLeft(layoutBox), -this.marginTop(layoutBox)));
82         absoluteContentBox.growBy(new LayoutSize(this.marginLeft(layoutBox) + this.marginRight(layoutBox), this.marginTop(layoutBox) + this.marginBottom(layoutBox)));
83         return absoluteContentBox;
84     }
85
86     absoluteBorderBox(layoutBox) {
87         let borderBox = this.toDisplayBox(layoutBox).borderBox();
88         let absoluteRect = new LayoutRect(this._toRootAbsolutePosition(layoutBox), borderBox.size());
89         absoluteRect.moveBy(borderBox.topLeft());
90         return absoluteRect;
91     }
92
93     absolutePaddingBox(layoutBox) {
94         let paddingBox = this.toDisplayBox(layoutBox).paddingBox();
95         let absoluteRect = new LayoutRect(this._toRootAbsolutePosition(layoutBox), paddingBox.size());
96         absoluteRect.moveBy(paddingBox.topLeft());
97         return absoluteRect;
98     }
99
100     absoluteContentBox(layoutBox) {
101         let contentBox = this.toDisplayBox(layoutBox).contentBox();
102         let absoluteRect = new LayoutRect(this._toRootAbsolutePosition(layoutBox), contentBox.size());
103         absoluteRect.moveBy(contentBox.topLeft());
104         return absoluteRect;
105     }
106
107     _toAbsolutePosition(position, layoutBox, container) {
108         // We should never need to go beyond the root container.
109         ASSERT(container == this.rootContainer() || container.isDescendantOf(this.rootContainer()));
110         let absolutePosition = position;
111         let ascendant = layoutBox.containingBlock();
112         while (ascendant && ascendant != container) {
113             ASSERT(ascendant.isDescendantOf(this.rootContainer()));
114             absolutePosition.moveBy(this.toDisplayBox(ascendant).topLeft());
115             ascendant = ascendant.containingBlock();
116         }
117         return absolutePosition;
118     }
119
120     _toRootAbsolutePosition(layoutBox) {
121         return this._toAbsolutePosition(this.toDisplayBox(layoutBox).topLeft(), layoutBox, this.rootContainer());
122     }
123
124     _descendantNeedsLayout() {
125         return this.m_layoutStack.length;
126     }
127
128     _addToLayoutQueue(layoutBox) {
129         // Initialize the corresponding display box.
130         this._createDisplayBox(layoutBox);
131         this.m_layoutStack.push(layoutBox);
132     }
133
134     _nextInLayoutQueue() {
135         ASSERT(this.m_layoutStack.length);
136         return this.m_layoutStack[this.m_layoutStack.length - 1];
137     }
138
139     _removeFromLayoutQueue(layoutBox) {
140         // With the current layout logic, the layoutBox should be at the top (this.m_layoutStack.pop() should do).
141         ASSERT(this.m_layoutStack.length);
142         ASSERT(this.m_layoutStack[this.m_layoutStack.length - 1] == layoutBox);
143         this.m_layoutStack.splice(this.m_layoutStack.indexOf(layoutBox), 1);
144     }
145
146     _createDisplayBox(layoutBox) {
147         let displayBox = new Display.Box(layoutBox.node());
148         this.m_displayToLayout.set(displayBox, layoutBox);
149         this.m_layoutToDisplay.set(layoutBox, displayBox);
150         // This is temporary.
151         layoutBox.setDisplayBox(displayBox);
152     }
153
154     toDisplayBox(layoutBox) {
155         ASSERT(layoutBox);
156         return layoutBox.displayBox();
157     }
158
159     toLayoutBox(displayBox) {
160         ASSERT(displayBox);
161         ASSERT(this.m_displayToLayout.has(displayBox));
162         return this.m_displayToLayout.get(displayBox);
163     }
164
165     _outOfFlowDescendants() {
166         // FIXME: This is highly inefficient but will do for now.
167         // 1. Collect all the out-of-flow descendants first.
168         // 2. Check if they are all belong to this formatting context.
169         //    - either the root container is the containing block.
170         //    - or a descendant of the root container is the containing block
171         //      and there is not other formatting context inbetween.
172         let allOutOfFlowBoxes = new Array();
173         let descendants = new Array();
174         for (let child = this.rootContainer().firstChild(); child; child = child.nextSibling())
175             descendants.push(child);
176         while (descendants.length) {
177             let descendant = descendants.pop();
178             if (descendant.isOutOfFlowPositioned())
179                 allOutOfFlowBoxes.push(descendant);
180             if (!descendant.isContainer())
181                 continue;
182             for (let child = descendant.lastChild(); child; child = child.previousSibling())
183                 descendants.push(child);
184         }
185         let outOfFlowBoxes = new Array();
186         for (let outOfFlowBox of allOutOfFlowBoxes) {
187             let containingBlock = outOfFlowBox.containingBlock();
188             // Collect the out-of-flow descendant that belong to this formatting context.
189             if (containingBlock == this.rootContainer())
190                 outOfFlowBoxes.push(outOfFlowBox);
191             else if (containingBlock.isDescendantOf(this.rootContainer())) {
192                 if (!containingBlock.establishesFormattingContext() || !containingBlock.isPositioned())
193                     outOfFlowBoxes.push(outOfFlowBox);
194             }
195         }
196         return outOfFlowBoxes;
197     }
198
199 }