2 * Copyright (C) 2018 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
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();
37 return this.m_rootContainer;
41 return this.m_layoutState;
45 return this.layoutState().layoutContext();
49 return this.m_floatingContext;
56 computeWidth(layoutBox) {
59 computeHeight(layoutBox) {
62 marginTop(layoutBox) {
63 return Utils.computedMarginTop(layoutBox.node());
66 marginLeft(layoutBox) {
67 return Utils.computedMarginLeft(layoutBox.node());
70 marginBottom(layoutBox) {
71 return Utils.computedMarginBottom(layoutBox.node());
74 marginRight(layoutBox) {
75 return Utils.computedMarginRight(layoutBox.node());
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;
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());
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());
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());
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();
117 return absolutePosition;
120 _toRootAbsolutePosition(layoutBox) {
121 return this._toAbsolutePosition(this.toDisplayBox(layoutBox).topLeft(), layoutBox, this.rootContainer());
124 _descendantNeedsLayout() {
125 return this.m_layoutStack.length;
128 _addToLayoutQueue(layoutBox) {
129 // Initialize the corresponding display box.
130 this._createDisplayBox(layoutBox);
131 this.m_layoutStack.push(layoutBox);
134 _nextInLayoutQueue() {
135 ASSERT(this.m_layoutStack.length);
136 return this.m_layoutStack[this.m_layoutStack.length - 1];
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);
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);
154 toDisplayBox(layoutBox) {
156 return layoutBox.displayBox();
159 toLayoutBox(displayBox) {
161 ASSERT(this.m_displayToLayout.has(displayBox));
162 return this.m_displayToLayout.get(displayBox);
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())
182 for (let child = descendant.lastChild(); child; child = child.previousSibling())
183 descendants.push(child);
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);
196 return outOfFlowBoxes;