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) {
28 this.m_rootContainer = rootContainer;
29 this.m_floatingContext = null;
30 this.m_displayToLayout = new Map();
31 this.m_layoutToDisplay = new Map();
32 this.m_layoutStack = new Array();
36 return this.m_rootContainer;
40 return this.m_floatingContext;
43 layout(layoutContext) {
46 computeWidth(layoutBox) {
49 computeHeight(layoutBox) {
52 marginTop(layoutBox) {
53 return Utils.computedMarginTop(layoutBox.node());
56 marginLeft(layoutBox) {
57 return Utils.computedMarginLeft(layoutBox.node());
60 marginBottom(layoutBox) {
61 return Utils.computedMarginBottom(layoutBox.node());
64 marginRight(layoutBox) {
65 return Utils.computedMarginRight(layoutBox.node());
68 absoluteMarginBox(layoutBox) {
69 let absoluteContentBox = Utils.mapToContainer(layoutBox, this.rootContainer());
70 absoluteContentBox.moveBy(new LayoutSize(-this.marginLeft(layoutBox), -this.marginTop(layoutBox)));
71 absoluteContentBox.growBy(new LayoutSize(this.marginLeft(layoutBox) + this.marginRight(layoutBox), this.marginTop(layoutBox) + this.marginBottom(layoutBox)));
72 return absoluteContentBox;
75 absoluteBorderBox(layoutBox) {
76 let borderBox = layoutBox.borderBox();
77 let absoluteRect = new LayoutRect(Utils.mapToContainer(layoutBox, this.rootContainer()).topLeft(), borderBox.size());
78 absoluteRect.moveBy(borderBox.topLeft());
82 absolutePaddingBox(layoutBox) {
83 let paddingBox = layoutBox.paddingBox();
84 let absoluteRect = new LayoutRect(Utils.mapToContainer(layoutBox, this.rootContainer()).topLeft(), paddingBox.size());
85 absoluteRect.moveBy(paddingBox.topLeft());
89 absoluteContentBox(layoutBox) {
90 let contentBox = layoutBox.contentBox();
91 let absoluteRect = new LayoutRect(Utils.mapToContainer(layoutBox, this.rootContainer()).topLeft(), contentBox.size());
92 absoluteRect.moveBy(contentBox.topLeft());
96 _toAbsolutePosition(layoutBox) {
97 // We should never need to go beyond the root container.
98 let containingBlock = layoutBox.containingBlock();
99 ASSERT(containingBlock == this.rootContainer() || containingBlock.isDescendantOf(this.rootContainer()));
100 let topLeft = layoutBox.rect().topLeft();
101 let ascendant = layoutBox.parent();
102 while (ascendant && ascendant != containingBlock) {
103 topLeft.moveBy(ascendant.rect().topLeft());
104 ascendant = ascendant.parent();
106 return new LayoutRect(topLeft, layoutBox.rect().size());
109 _descendantNeedsLayout() {
110 return this.m_layoutStack.length;
113 _addToLayoutQueue(layoutBox) {
114 // Initialize the corresponding display box.
115 this._createDisplayBox(layoutBox);
116 this.m_layoutStack.push(layoutBox);
119 _nextInLayoutQueue() {
120 ASSERT(this.m_layoutStack.length);
121 return this.m_layoutStack[this.m_layoutStack.length - 1];
124 _removeFromLayoutQueue(layoutBox) {
125 // With the current layout logic, the layoutBox should be at the top (this.m_layoutStack.pop() should do).
126 ASSERT(this.m_layoutStack.length);
127 ASSERT(this.m_layoutStack[this.m_layoutStack.length - 1] == layoutBox);
128 this.m_layoutStack.splice(this.m_layoutStack.indexOf(layoutBox), 1);
131 _createDisplayBox(layoutBox) {
132 let displayBox = new Display.Box(layoutBox.node());
133 this.m_displayToLayout.set(displayBox, layoutBox);
134 this.m_layoutToDisplay.set(layoutBox, displayBox);
135 // This is temporary.
136 layoutBox.setDisplayBox(displayBox);
139 toDisplayBox(layoutBox) {
141 ASSERT(this.m_layoutToDisplay.has(layoutBox));
142 return this.m_layoutToDisplay.get(layoutBox);
145 toLayoutBox(displayBox) {
147 ASSERT(this.m_displayToLayout.has(displayBox));
148 return this.m_displayToLayout.get(displayBox);
151 _outOfFlowDescendants() {
152 // FIXME: This is highly inefficient but will do for now.
153 // 1. Collect all the out-of-flow descendants first.
154 // 2. Check if they are all belong to this formatting context.
155 // - either the root container is the containing block.
156 // - or a descendant of the root container is the containing block
157 // and there is not other formatting context inbetween.
158 let allOutOfFlowBoxes = new Array();
159 let descendants = new Array();
160 for (let child = this.rootContainer().firstChild(); child; child = child.nextSibling())
161 descendants.push(child);
162 while (descendants.length) {
163 let descendant = descendants.pop();
164 if (descendant.isOutOfFlowPositioned())
165 allOutOfFlowBoxes.push(descendant);
166 if (!descendant.isContainer())
168 for (let child = descendant.lastChild(); child; child = child.previousSibling())
169 descendants.push(child);
171 let outOfFlowBoxes = new Array();
172 for (let outOfFlowBox of allOutOfFlowBoxes) {
173 let containingBlock = outOfFlowBox.containingBlock();
174 // Collect the out-of-flow descendant that belong to this formatting context.
175 if (containingBlock == this.rootContainer())
176 outOfFlowBoxes.push(outOfFlowBox);
177 else if (containingBlock.isDescendantOf(this.rootContainer())) {
178 if (!containingBlock.establishedFormattingContext() || !containingBlock.isPositioned())
179 outOfFlowBoxes.push(outOfFlowBox);
182 return outOfFlowBoxes;