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