zalan@apple.com | 903aad7 | 2018-03-09 19:10:37 +0000 | [diff] [blame] | 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 | |
zalan@apple.com | c2fa069 | 2018-04-14 04:48:35 +0000 | [diff] [blame] | 26 | /* |
| 27 | class FormattingContext { |
| 28 | public: |
| 29 | Layout::Container& formattingRoot(); |
| 30 | FormattingState& formattingState(); |
| 31 | LayoutState& layoutState(); |
| 32 | FloatingContext& floatingContext(); |
| 33 | |
| 34 | virtual void layout(); |
| 35 | |
| 36 | virtual void computeWidth(const Layout::Box&); |
| 37 | virtual void computeHeight(const Layout::Box&); |
| 38 | |
| 39 | virtual LayoutUnit marginTop(const Layout::Box&); |
| 40 | virtual LayoutUnit marginLeft(const Layout::Box&); |
| 41 | virtual LayoutUnit marginBottom(const Layout::Box&); |
| 42 | virtual LayoutUnit marginRight(const Layout::Box&); |
| 43 | |
| 44 | private: |
| 45 | void computeFloatingWidth(const Layout::Box&); |
| 46 | void computeFloatingHeight(const Layout::Box&); |
| 47 | |
| 48 | void placeInFlowPositionedChildren(const Layout::Box&); |
| 49 | void computeInFlowPositionedPosition(const Layout::Box&); |
| 50 | void computeInFlowWidth(const Layout::Box&); |
| 51 | |
| 52 | void layoutOutOfFlowDescendants(); |
| 53 | |
| 54 | void computeOutOfFlowWidth(const Layout::Box&); |
| 55 | void computeOutOfFlowHeight(const Layout::Box&); |
| 56 | void computeOutOfFlowPosition(const Layout::Box&); |
| 57 | |
| 58 | LayoutUnit shrinkToFitWidth(Layout::Box&); |
| 59 | }; |
| 60 | */ |
zalan@apple.com | 903aad7 | 2018-03-09 19:10:37 +0000 | [diff] [blame] | 61 | class FormattingContext { |
zalan@apple.com | f5e498a | 2018-03-21 04:42:32 +0000 | [diff] [blame] | 62 | constructor(formattingState) { |
| 63 | this.m_formattingState = formattingState; |
zalan@apple.com | 94319d4 | 2018-04-01 02:44:49 +0000 | [diff] [blame] | 64 | this.m_floatingContext = new FloatingContext(formattingState.floatingState()); |
zalan@apple.com | 4d9437e | 2018-03-17 04:34:02 +0000 | [diff] [blame] | 65 | this.m_layoutStack = new Array(); |
zalan@apple.com | 903aad7 | 2018-03-09 19:10:37 +0000 | [diff] [blame] | 66 | } |
| 67 | |
zalan@apple.com | f5e498a | 2018-03-21 04:42:32 +0000 | [diff] [blame] | 68 | formattingRoot() { |
| 69 | return this.m_formattingState.formattingRoot(); |
| 70 | } |
| 71 | |
| 72 | formattingState() { |
| 73 | return this.m_formattingState; |
zalan@apple.com | 903aad7 | 2018-03-09 19:10:37 +0000 | [diff] [blame] | 74 | } |
| 75 | |
zalan@apple.com | 6702223 | 2018-03-20 15:12:33 +0000 | [diff] [blame] | 76 | layoutState() { |
zalan@apple.com | f5e498a | 2018-03-21 04:42:32 +0000 | [diff] [blame] | 77 | return this.formattingState().layoutState(); |
zalan@apple.com | ae809a7 | 2018-03-20 05:02:41 +0000 | [diff] [blame] | 78 | } |
| 79 | |
zalan@apple.com | 903aad7 | 2018-03-09 19:10:37 +0000 | [diff] [blame] | 80 | floatingContext() { |
| 81 | return this.m_floatingContext; |
| 82 | } |
| 83 | |
zalan@apple.com | 6702223 | 2018-03-20 15:12:33 +0000 | [diff] [blame] | 84 | layout() { |
| 85 | ASSERT_NOT_REACHED(); |
zalan@apple.com | 903aad7 | 2018-03-09 19:10:37 +0000 | [diff] [blame] | 86 | } |
| 87 | |
zalan@apple.com | 5fd41f8 | 2018-03-15 15:59:46 +0000 | [diff] [blame] | 88 | computeWidth(layoutBox) { |
zalan@apple.com | 903aad7 | 2018-03-09 19:10:37 +0000 | [diff] [blame] | 89 | } |
| 90 | |
zalan@apple.com | 5fd41f8 | 2018-03-15 15:59:46 +0000 | [diff] [blame] | 91 | computeHeight(layoutBox) { |
zalan@apple.com | 903aad7 | 2018-03-09 19:10:37 +0000 | [diff] [blame] | 92 | } |
| 93 | |
zalan@apple.com | 5fd41f8 | 2018-03-15 15:59:46 +0000 | [diff] [blame] | 94 | marginTop(layoutBox) { |
zalan@apple.com | e1fa8e8 | 2018-03-16 14:52:42 +0000 | [diff] [blame] | 95 | return Utils.computedMarginTop(layoutBox.node()); |
zalan@apple.com | 903aad7 | 2018-03-09 19:10:37 +0000 | [diff] [blame] | 96 | } |
| 97 | |
zalan@apple.com | 5fd41f8 | 2018-03-15 15:59:46 +0000 | [diff] [blame] | 98 | marginLeft(layoutBox) { |
zalan@apple.com | e1fa8e8 | 2018-03-16 14:52:42 +0000 | [diff] [blame] | 99 | return Utils.computedMarginLeft(layoutBox.node()); |
zalan@apple.com | 903aad7 | 2018-03-09 19:10:37 +0000 | [diff] [blame] | 100 | } |
| 101 | |
zalan@apple.com | 5fd41f8 | 2018-03-15 15:59:46 +0000 | [diff] [blame] | 102 | marginBottom(layoutBox) { |
zalan@apple.com | e1fa8e8 | 2018-03-16 14:52:42 +0000 | [diff] [blame] | 103 | return Utils.computedMarginBottom(layoutBox.node()); |
zalan@apple.com | 903aad7 | 2018-03-09 19:10:37 +0000 | [diff] [blame] | 104 | } |
| 105 | |
zalan@apple.com | 5fd41f8 | 2018-03-15 15:59:46 +0000 | [diff] [blame] | 106 | marginRight(layoutBox) { |
zalan@apple.com | e1fa8e8 | 2018-03-16 14:52:42 +0000 | [diff] [blame] | 107 | return Utils.computedMarginRight(layoutBox.node()); |
zalan@apple.com | 903aad7 | 2018-03-09 19:10:37 +0000 | [diff] [blame] | 108 | } |
| 109 | |
zalan@apple.com | 53972bd | 2018-04-12 17:26:55 +0000 | [diff] [blame] | 110 | static isInFormattingContext(layoutBox, formattingContextRoot) { |
| 111 | ASSERT(formattingContextRoot.establishesFormattingContext()); |
| 112 | // If we hit the "this" while climbing up on the containing block chain and we don't pass a formatting context root -> box is part of this box's formatting context. |
| 113 | for (let containingBlock = layoutBox.containingBlock(); containingBlock; containingBlock = containingBlock.containingBlock()) { |
| 114 | if (containingBlock == formattingContextRoot) |
| 115 | return true; |
| 116 | if (containingBlock.establishesFormattingContext()) |
| 117 | return false; |
| 118 | } |
| 119 | return false; |
| 120 | } |
| 121 | |
zalan@apple.com | 4d9437e | 2018-03-17 04:34:02 +0000 | [diff] [blame] | 122 | _descendantNeedsLayout() { |
| 123 | return this.m_layoutStack.length; |
| 124 | } |
| 125 | |
| 126 | _addToLayoutQueue(layoutBox) { |
zalan@apple.com | 91a50c2 | 2018-04-08 05:27:36 +0000 | [diff] [blame] | 127 | if (!layoutBox) |
| 128 | return; |
zalan@apple.com | 4d9437e | 2018-03-17 04:34:02 +0000 | [diff] [blame] | 129 | // Initialize the corresponding display box. |
zalan@apple.com | 1e49017 | 2018-04-01 00:50:13 +0000 | [diff] [blame] | 130 | let displayBox = this.formattingState().createDisplayBox(layoutBox, this); |
| 131 | if (layoutBox.node()) { |
| 132 | displayBox.setMarginTop(this.marginTop(layoutBox)); |
| 133 | displayBox.setMarginLeft(this.marginLeft(layoutBox)); |
| 134 | displayBox.setMarginBottom(this.marginBottom(layoutBox)); |
| 135 | displayBox.setMarginRight(this.marginRight(layoutBox)); |
| 136 | } |
| 137 | |
zalan@apple.com | 4d9437e | 2018-03-17 04:34:02 +0000 | [diff] [blame] | 138 | this.m_layoutStack.push(layoutBox); |
| 139 | } |
| 140 | |
| 141 | _nextInLayoutQueue() { |
| 142 | ASSERT(this.m_layoutStack.length); |
| 143 | return this.m_layoutStack[this.m_layoutStack.length - 1]; |
| 144 | } |
| 145 | |
| 146 | _removeFromLayoutQueue(layoutBox) { |
| 147 | // With the current layout logic, the layoutBox should be at the top (this.m_layoutStack.pop() should do). |
| 148 | ASSERT(this.m_layoutStack.length); |
| 149 | ASSERT(this.m_layoutStack[this.m_layoutStack.length - 1] == layoutBox); |
| 150 | this.m_layoutStack.splice(this.m_layoutStack.indexOf(layoutBox), 1); |
| 151 | } |
| 152 | |
zalan@apple.com | edb1430 | 2018-03-20 16:14:28 +0000 | [diff] [blame] | 153 | displayBox(layoutBox) { |
zalan@apple.com | f5e498a | 2018-03-21 04:42:32 +0000 | [diff] [blame] | 154 | return this.formattingState().displayBox(layoutBox); |
zalan@apple.com | 4d9437e | 2018-03-17 04:34:02 +0000 | [diff] [blame] | 155 | } |
zalan@apple.com | adf6618 | 2018-03-18 16:41:46 +0000 | [diff] [blame] | 156 | |
zalan@apple.com | 589be6c | 2018-04-04 04:36:36 +0000 | [diff] [blame] | 157 | _computeFloatingWidth(layoutBox) { |
| 158 | // FIXME: missing cases |
| 159 | this.displayBox(layoutBox).setWidth(Utils.width(layoutBox) + Utils.computedHorizontalBorderAndPadding(layoutBox.node())); |
| 160 | } |
| 161 | |
| 162 | _computeFloatingHeight(layoutBox) { |
| 163 | // FIXME: missing cases |
| 164 | this.displayBox(layoutBox).setHeight(Utils.height(layoutBox) + Utils.computedVerticalBorderAndPadding(layoutBox.node())); |
| 165 | } |
| 166 | |
zalan@apple.com | fc7c26d | 2018-04-10 17:21:50 +0000 | [diff] [blame] | 167 | _placeInFlowPositionedChildren(container) { |
| 168 | if (!container.isContainer()) |
| 169 | return; |
| 170 | // If this layoutBox also establishes a formatting context, then positioning already has happend at the formatting context. |
| 171 | if (container.establishesFormattingContext() && container != this.formattingRoot()) |
| 172 | return; |
| 173 | ASSERT(container.isContainer()); |
| 174 | for (let inFlowChild = container.firstInFlowChild(); inFlowChild; inFlowChild = inFlowChild.nextInFlowSibling()) { |
| 175 | if (!inFlowChild.isInFlowPositioned()) |
| 176 | continue; |
| 177 | this._computeInFlowPositionedPosition(inFlowChild); |
| 178 | } |
| 179 | } |
| 180 | |
zalan@apple.com | 49fae34 | 2018-04-10 05:06:37 +0000 | [diff] [blame] | 181 | _computeInFlowPositionedPosition(layoutBox) { |
| 182 | // Start with the original, static position. |
| 183 | let displayBox = this.displayBox(layoutBox); |
| 184 | let relativePosition = displayBox.topLeft(); |
| 185 | // Top/bottom |
| 186 | if (!Utils.isTopAuto(layoutBox)) |
| 187 | relativePosition.shiftTop(Utils.top(layoutBox)); |
| 188 | else if (!Utils.isBottomAuto(layoutBox)) |
| 189 | relativePosition.shiftTop(-Utils.bottom(layoutBox)); |
| 190 | // Left/right |
| 191 | if (!Utils.isLeftAuto(layoutBox)) |
| 192 | relativePosition.shiftLeft(Utils.left(layoutBox)); |
| 193 | else if (!Utils.isRightAuto(layoutBox)) |
| 194 | relativePosition.shiftLeft(-Utils.right(layoutBox)); |
| 195 | displayBox.setTopLeft(relativePosition); |
| 196 | } |
| 197 | |
zalan@apple.com | fc7c26d | 2018-04-10 17:21:50 +0000 | [diff] [blame] | 198 | _computeInFlowWidth(layoutBox) { |
| 199 | if (Utils.isWidthAuto(layoutBox)) |
| 200 | return this.displayBox(layoutBox).setWidth(this._horizontalConstraint(layoutBox)); |
| 201 | return this.displayBox(layoutBox).setWidth(Utils.width(layoutBox) + Utils.computedHorizontalBorderAndPadding(layoutBox.node())); |
| 202 | } |
| 203 | |
| 204 | _layoutOutOfFlowDescendants() { |
| 205 | // This lays out all the out-of-flow boxes that belong to this formatting context even if |
| 206 | // the root container is not the containing block. |
| 207 | let outOfFlowDescendants = this._outOfFlowDescendants(); |
| 208 | for (let outOfFlowBox of outOfFlowDescendants) { |
| 209 | this._addToLayoutQueue(outOfFlowBox); |
| 210 | this._computeOutOfFlowWidth(outOfFlowBox); |
zalan@apple.com | 5a5aa91 | 2018-04-13 03:09:03 +0000 | [diff] [blame] | 211 | this.layoutState().formattingContext(outOfFlowBox).layout(); |
zalan@apple.com | fc7c26d | 2018-04-10 17:21:50 +0000 | [diff] [blame] | 212 | this._computeOutOfFlowHeight(outOfFlowBox); |
| 213 | this._computeOutOfFlowPosition(outOfFlowBox); |
| 214 | this._removeFromLayoutQueue(outOfFlowBox); |
zalan@apple.com | 0d2ec3b | 2018-04-12 04:19:30 +0000 | [diff] [blame] | 215 | this.formattingState().clearNeedsLayout(outOfFlowBox); |
zalan@apple.com | 49fae34 | 2018-04-10 05:06:37 +0000 | [diff] [blame] | 216 | } |
| 217 | } |
| 218 | |
zalan@apple.com | fc7c26d | 2018-04-10 17:21:50 +0000 | [diff] [blame] | 219 | _computeOutOfFlowWidth(layoutBox) { |
| 220 | // 10.3.7 Absolutely positioned, non-replaced elements |
| 221 | |
| 222 | // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the width is shrink-to-fit. Then solve for 'left' |
| 223 | // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if the 'direction' property of the element establishing |
| 224 | // the static-position containing block is 'ltr' set 'left' to the static position, otherwise set 'right' to the static position. |
| 225 | // Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr'). |
| 226 | // 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right' |
| 227 | // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left' |
| 228 | // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width' |
| 229 | // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right' |
| 230 | let width = Number.NaN; |
| 231 | if (Utils.isWidthAuto(layoutBox) && Utils.isLeftAuto(layoutBox) && Utils.isRightAuto(layoutBox)) |
| 232 | width = this._shrinkToFitWidth(layoutBox); |
| 233 | else if (Utils.isLeftAuto(layoutBox) && Utils.isWidthAuto(layoutBox) && !Utils.isRightAuto(layoutBox)) |
| 234 | width = this._shrinkToFitWidth(layoutBox); // 1 |
| 235 | else if (Utils.isLeftAuto(layoutBox) && Utils.isRightAuto(layoutBox) && !Utils.isWidthAuto(layoutBox)) |
| 236 | width = Utils.width(layoutBox); // 2 |
| 237 | else if (Utils.isWidthAuto(layoutBox) && Utils.isRightAuto(layoutBox) && !Utils.isLeftAuto(layoutBox)) |
| 238 | width = this._shrinkToFitWidth(layoutBox); // 3 |
| 239 | else if (Utils.isLeftAuto(layoutBox) && !Utils.isWidthAuto(layoutBox) && !Utils.isRightAuto(layoutBox)) |
| 240 | width = Utils.width(layoutBox); // 4 |
| 241 | else if (Utils.isWidthAuto(layoutBox) && !Utils.isLeftAuto(layoutBox) && !Utils.isRightAuto(layoutBox)) |
| 242 | width = Math.max(0, this.displayBox(layoutBox.containingBlock()).contentBox().width() - Utils.right(layoutBox) - Utils.left(layoutBox)); // 5 |
| 243 | else if (Utils.isRightAuto(layoutBox) && !Utils.isLeftAuto(layoutBox) && !Utils.isWidthAuto(layoutBox)) |
| 244 | width = Utils.width(layoutBox); // 6 |
| 245 | else |
| 246 | ASSERT_NOT_REACHED(); |
| 247 | width += Utils.computedHorizontalBorderAndPadding(layoutBox.node()); |
| 248 | this.displayBox(layoutBox).setWidth(width); |
| 249 | } |
| 250 | |
| 251 | _computeOutOfFlowHeight(layoutBox) { |
| 252 | // 1. If all three of 'top', 'height', and 'bottom' are auto, set 'top' to the static position and apply rule number three below. |
| 253 | // 2. If none of the three are 'auto': If both 'margin-top' and 'margin-bottom' are 'auto', solve the equation under |
| 254 | // the extra constraint that the two margins get equal values. If one of 'margin-top' or 'margin-bottom' is 'auto', |
| 255 | // solve the equation for that value. If the values are over-constrained, ignore the value for 'bottom' and solve for that value. |
| 256 | // Otherwise, pick the one of the following six rules that applies. |
| 257 | |
| 258 | // 3. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then the height is based on the content per 10.6.7, |
| 259 | // set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'top' |
| 260 | // 4. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then set 'top' to the static position, set 'auto' values |
| 261 | // for 'margin-top' and 'margin-bottom' to 0, and solve for 'bottom' |
| 262 | // 5. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then the height is based on the content per 10.6.7, |
| 263 | // set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'bottom' |
| 264 | // 6. 'top' is 'auto', 'height' and 'bottom' are not 'auto', then set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'top' |
| 265 | // 7. 'height' is 'auto', 'top' and 'bottom' are not 'auto', then 'auto' values for 'margin-top' and 'margin-bottom' are set to 0 and solve for 'height' |
| 266 | // 8. 'bottom' is 'auto', 'top' and 'height' are not 'auto', then set 'auto' values for 'margin-top' and 'margin-bottom' to 0 and solve for 'bottom' |
| 267 | let height = Number.NaN; |
| 268 | if (Utils.isHeightAuto(layoutBox) && Utils.isBottomAuto(layoutBox) && Utils.isTopAuto(layoutBox)) |
| 269 | height = this._contentHeight(layoutBox); // 1 |
| 270 | else if (Utils.isTopAuto((layoutBox)) && Utils.isHeightAuto((layoutBox)) && !Utils.isBottomAuto((layoutBox))) |
| 271 | height = this._contentHeight(layoutBox); // 3 |
| 272 | else if (Utils.isTopAuto((layoutBox)) && Utils.isBottomAuto((layoutBox)) && !Utils.isHeightAuto((layoutBox))) |
| 273 | height = Utils.height(layoutBox); // 4 |
| 274 | else if (Utils.isHeightAuto((layoutBox)) && Utils.isBottomAuto((layoutBox)) && !Utils.isTopAuto((layoutBox))) |
| 275 | height = this._contentHeight(layoutBox); // 5 |
| 276 | else if (Utils.isTopAuto((layoutBox)) && !Utils.isHeightAuto((layoutBox)) && !Utils.isBottomAuto((layoutBox))) |
| 277 | height = Utils.height(layoutBox); // 6 |
| 278 | else if (Utils.isHeightAuto((layoutBox)) && !Utils.isTopAuto((layoutBox)) && !Utils.isBottomAuto((layoutBox))) |
| 279 | height = Math.max(0, this.displayBox(layoutBox.containingBlock()).contentBox().height() - Utils.bottom(layoutBox) - Utils.top(layoutBox)); // 7 |
| 280 | else if (Utils.isBottomAuto((layoutBox)) && !Utils.isTopAuto((layoutBox)) && !Utils.isHeightAuto((layoutBox))) |
| 281 | height = Utils.height(layoutBox); // 8 |
| 282 | else |
| 283 | ASSERT_NOT_REACHED(); |
| 284 | height += Utils.computedVerticalBorderAndPadding(layoutBox.node()); |
| 285 | this.displayBox(layoutBox).setHeight(height); |
| 286 | } |
| 287 | |
| 288 | _computeOutOfFlowPosition(layoutBox) { |
| 289 | let displayBox = this.displayBox(layoutBox); |
| 290 | let top = Number.NaN; |
| 291 | let containerSize = this.displayBox(layoutBox.containingBlock()).contentBox().size(); |
| 292 | // Top/bottom |
| 293 | if (Utils.isTopAuto(layoutBox) && Utils.isBottomAuto(layoutBox)) { |
| 294 | ASSERT(Utils.isStaticallyPositioned(layoutBox)); |
| 295 | // Vertically statically positioned. |
| 296 | // FIXME: Figure out if it is actually valid that we use the parent box as the container (which is not even in this formatting context). |
| 297 | let parent = layoutBox.parent(); |
| 298 | let parentDisplayBox = this.displayBox(parent); |
| 299 | let previousInFlowSibling = layoutBox.previousInFlowSibling(); |
| 300 | let contentBottom = previousInFlowSibling ? this.displayBox(previousInFlowSibling).bottom() : parentDisplayBox.contentBox().top(); |
| 301 | top = contentBottom + this.marginTop(layoutBox); |
| 302 | // Convert static position (in parent coordinate system) to absolute (in containing block coordindate system). |
| 303 | if (parent != layoutBox.containingBlock()) { |
| 304 | ASSERT(displayBox.parent() == this.displayBox(layoutBox.containingBlock())); |
| 305 | top += Utils.mapPosition(parentDisplayBox.topLeft(), parentDisplayBox, displayBox.parent()).top(); |
| 306 | } |
| 307 | } else if (!Utils.isTopAuto(layoutBox)) |
| 308 | top = Utils.top(layoutBox) + this.marginTop(layoutBox); |
| 309 | else if (!Utils.isBottomAuto(layoutBox)) |
| 310 | top = containerSize.height() - Utils.bottom(layoutBox) - displayBox.height() - this.marginBottom(layoutBox); |
| 311 | else |
| 312 | ASSERT_NOT_REACHED(); |
| 313 | // Left/right |
| 314 | let left = Number.NaN; |
| 315 | if (Utils.isLeftAuto(layoutBox) && Utils.isRightAuto(layoutBox)) { |
| 316 | ASSERT(Utils.isStaticallyPositioned(layoutBox)); |
| 317 | // Horizontally statically positioned. |
| 318 | // FIXME: Figure out if it is actually valid that we use the parent box as the container (which is not even in this formatting context). |
| 319 | let parent = layoutBox.parent(); |
| 320 | let parentDisplayBox = this.displayBox(parent); |
| 321 | left = parentDisplayBox.contentBox().left() + this.marginLeft(layoutBox); |
| 322 | // Convert static position (in parent coordinate system) to absolute (in containing block coordindate system). |
| 323 | if (parent != layoutBox.containingBlock()) { |
| 324 | ASSERT(displayBox.parent() == this.displayBox(layoutBox.containingBlock())); |
| 325 | left += Utils.mapPosition(parentDisplayBox.topLeft(), parentDisplayBox, displayBox.parent()).left(); |
| 326 | } |
| 327 | } else if (!Utils.isLeftAuto(layoutBox)) |
| 328 | left = Utils.left(layoutBox) + this.marginLeft(layoutBox); |
| 329 | else if (!Utils.isRightAuto(layoutBox)) |
| 330 | left = containerSize.width() - Utils.right(layoutBox) - displayBox.width() - this.marginRight(layoutBox); |
| 331 | else |
| 332 | ASSERT_NOT_REACHED(); |
| 333 | displayBox.setTopLeft(new LayoutPoint(top, left)); |
| 334 | } |
| 335 | |
| 336 | _shrinkToFitWidth(layoutBox) { |
| 337 | // FIXME: this is naive and missing the actual preferred width computation. |
| 338 | ASSERT(Utils.isWidthAuto(layoutBox)); |
| 339 | if (!layoutBox.isContainer() || !layoutBox.hasChild()) |
| 340 | return 0; |
| 341 | let width = 0; |
| 342 | for (let inFlowChild = layoutBox.firstInFlowChild(); inFlowChild; inFlowChild = inFlowChild.nextInFlowSibling()) { |
| 343 | let widthCandidate = Utils.isWidthAuto(inFlowChild) ? this._shrinkToFitWidth(inFlowChild) : Utils.width(inFlowChild); |
| 344 | width = Math.max(width, widthCandidate + Utils.computedHorizontalBorderAndPadding(inFlowChild.node())); |
| 345 | } |
| 346 | return width; |
| 347 | } |
| 348 | |
zalan@apple.com | adf6618 | 2018-03-18 16:41:46 +0000 | [diff] [blame] | 349 | _outOfFlowDescendants() { |
| 350 | // FIXME: This is highly inefficient but will do for now. |
| 351 | // 1. Collect all the out-of-flow descendants first. |
| 352 | // 2. Check if they are all belong to this formatting context. |
| 353 | // - either the root container is the containing block. |
| 354 | // - or a descendant of the root container is the containing block |
| 355 | // and there is not other formatting context inbetween. |
| 356 | let allOutOfFlowBoxes = new Array(); |
| 357 | let descendants = new Array(); |
zalan@apple.com | f5e498a | 2018-03-21 04:42:32 +0000 | [diff] [blame] | 358 | for (let child = this.formattingRoot().firstChild(); child; child = child.nextSibling()) |
zalan@apple.com | adf6618 | 2018-03-18 16:41:46 +0000 | [diff] [blame] | 359 | descendants.push(child); |
| 360 | while (descendants.length) { |
| 361 | let descendant = descendants.pop(); |
| 362 | if (descendant.isOutOfFlowPositioned()) |
| 363 | allOutOfFlowBoxes.push(descendant); |
| 364 | if (!descendant.isContainer()) |
| 365 | continue; |
| 366 | for (let child = descendant.lastChild(); child; child = child.previousSibling()) |
| 367 | descendants.push(child); |
| 368 | } |
| 369 | let outOfFlowBoxes = new Array(); |
| 370 | for (let outOfFlowBox of allOutOfFlowBoxes) { |
| 371 | let containingBlock = outOfFlowBox.containingBlock(); |
| 372 | // Collect the out-of-flow descendant that belong to this formatting context. |
zalan@apple.com | f5e498a | 2018-03-21 04:42:32 +0000 | [diff] [blame] | 373 | if (containingBlock == this.formattingRoot()) |
zalan@apple.com | adf6618 | 2018-03-18 16:41:46 +0000 | [diff] [blame] | 374 | outOfFlowBoxes.push(outOfFlowBox); |
zalan@apple.com | f5e498a | 2018-03-21 04:42:32 +0000 | [diff] [blame] | 375 | else if (containingBlock.isDescendantOf(this.formattingRoot())) { |
zalan@apple.com | ae809a7 | 2018-03-20 05:02:41 +0000 | [diff] [blame] | 376 | if (!containingBlock.establishesFormattingContext() || !containingBlock.isPositioned()) |
zalan@apple.com | adf6618 | 2018-03-18 16:41:46 +0000 | [diff] [blame] | 377 | outOfFlowBoxes.push(outOfFlowBox); |
| 378 | } |
| 379 | } |
| 380 | return outOfFlowBoxes; |
| 381 | } |
zalan@apple.com | c655317 | 2018-04-12 14:51:35 +0000 | [diff] [blame] | 382 | |
| 383 | _firstInFlowChildWithNeedsLayout(layoutBox) { |
| 384 | if (!layoutBox.isContainer()) |
| 385 | return null; |
| 386 | for (let child = layoutBox.firstInFlowOrFloatChild(); child; child = child.nextInFlowOrFloatSibling()) { |
| 387 | if (this.formattingState().needsLayout(child)) |
| 388 | return child; |
| 389 | } |
| 390 | return null; |
| 391 | } |
| 392 | |
| 393 | _nextInFlowSiblingWithNeedsLayout(layoutBox) { |
| 394 | for (let sibling = layoutBox.nextInFlowOrFloatSibling(); sibling; sibling = sibling.nextInFlowOrFloatSibling()) { |
| 395 | if (this.formattingState().needsLayout(sibling)) |
| 396 | return sibling; |
| 397 | } |
| 398 | return null; |
| 399 | } |
zalan@apple.com | 903aad7 | 2018-03-09 19:10:37 +0000 | [diff] [blame] | 400 | } |