| 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. AND ITS CONTRIBUTORS ``AS IS'' |
| 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| 15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| 17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| 23 | * THE POSSIBILITY OF SUCH DAMAGE. |
| 24 | */ |
| 25 | |
| 26 | #include "config.h" |
| 27 | #include "BlockFormattingContext.h" |
| 28 | |
| 29 | #if ENABLE(LAYOUT_FORMATTING_CONTEXT) |
| 30 | |
| 31 | #include "FormattingContext.h" |
| 32 | #include "InlineFormattingState.h" |
| 33 | #include "LayoutChildIterator.h" |
| 34 | #include "Logging.h" |
| 35 | #include <wtf/text/TextStream.h> |
| 36 | |
| 37 | namespace WebCore { |
| 38 | namespace Layout { |
| 39 | |
| 40 | HeightAndMargin BlockFormattingContext::Geometry::inFlowNonReplacedHeightAndMargin(const LayoutState& layoutState, const Box& layoutBox, UsedVerticalValues usedValues) |
| 41 | { |
| 42 | ASSERT(layoutBox.isInFlow() && !layoutBox.replaced()); |
| 43 | ASSERT(layoutBox.isOverflowVisible()); |
| 44 | |
| 45 | auto compute = [&]() -> HeightAndMargin { |
| 46 | |
| 47 | // 10.6.3 Block-level non-replaced elements in normal flow when 'overflow' computes to 'visible' |
| 48 | // |
| 49 | // If 'margin-top', or 'margin-bottom' are 'auto', their used value is 0. |
| 50 | // If 'height' is 'auto', the height depends on whether the element has any block-level children and whether it has padding or borders: |
| 51 | // The element's height is the distance from its top content edge to the first applicable of the following: |
| 52 | // 1. the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines |
| 53 | // 2. the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin |
| 54 | // does not collapse with the element's bottom margin |
| 55 | // 3. the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin |
| 56 | // 4. zero, otherwise |
| 57 | // Only children in the normal flow are taken into account (i.e., floating boxes and absolutely positioned boxes are ignored, |
| 58 | // and relatively positioned boxes are considered without their offset). Note that the child box may be an anonymous block box. |
| 59 | |
| 60 | auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox); |
| 61 | auto containingBlockWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth(); |
| 62 | auto computedVerticalMargin = Geometry::computedVerticalMargin(layoutBox, UsedHorizontalValues { containingBlockWidth }); |
| 63 | auto nonCollapsedMargin = UsedVerticalMargin::NonCollapsedValues { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) }; |
| 64 | auto borderAndPaddingTop = displayBox.borderTop() + displayBox.paddingTop().valueOr(0); |
| 65 | auto height = usedValues.height ? usedValues.height.value() : computedHeightValue(layoutState, layoutBox, HeightType::Normal); |
| 66 | |
| 67 | if (height) { |
| 68 | auto borderAndPaddingBottom = displayBox.borderBottom() + displayBox.paddingBottom().valueOr(0); |
| 69 | auto contentHeight = layoutBox.style().boxSizing() == BoxSizing::ContentBox ? *height : *height - (borderAndPaddingTop + borderAndPaddingBottom); |
| 70 | return { contentHeight, nonCollapsedMargin }; |
| 71 | } |
| 72 | |
| 73 | if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild()) |
| 74 | return { 0, nonCollapsedMargin }; |
| 75 | |
| 76 | // 1. the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines |
| 77 | if (layoutBox.establishesInlineFormattingContext()) { |
| 78 | // This is temp and will be replaced by the correct display box once inline runs move over to the display tree. |
| 79 | auto& inlineRuns = downcast<InlineFormattingState>(layoutState.establishedFormattingState(layoutBox)).inlineRuns(); |
| 80 | auto bottomEdge = inlineRuns.isEmpty() ? LayoutUnit() : inlineRuns.last().logicalBottom(); |
| 81 | return { bottomEdge - borderAndPaddingTop, nonCollapsedMargin }; |
| 82 | } |
| 83 | |
| 84 | // 2. the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin... |
| 85 | auto* lastInFlowChild = downcast<Container>(layoutBox).lastInFlowChild(); |
| 86 | ASSERT(lastInFlowChild); |
| 87 | if (!MarginCollapse::marginAfterCollapsesWithParentMarginAfter(layoutState, *lastInFlowChild)) { |
| 88 | auto& lastInFlowDisplayBox = layoutState.displayBoxForLayoutBox(*lastInFlowChild); |
| 89 | auto bottomEdgeOfBottomMargin = lastInFlowDisplayBox.bottom() + (lastInFlowDisplayBox.hasCollapsedThroughMargin() ? LayoutUnit() : lastInFlowDisplayBox.marginAfter()); |
| 90 | return { bottomEdgeOfBottomMargin - borderAndPaddingTop, nonCollapsedMargin }; |
| 91 | } |
| 92 | |
| 93 | // 3. the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin |
| 94 | auto* inFlowChild = lastInFlowChild; |
| 95 | while (inFlowChild && MarginCollapse::marginBeforeCollapsesWithParentMarginAfter(layoutState, *inFlowChild)) |
| 96 | inFlowChild = inFlowChild->previousInFlowSibling(); |
| 97 | if (inFlowChild) { |
| 98 | auto& inFlowDisplayBox = layoutState.displayBoxForLayoutBox(*inFlowChild); |
| 99 | return { inFlowDisplayBox.top() + inFlowDisplayBox.borderBox().height() - borderAndPaddingTop, nonCollapsedMargin }; |
| 100 | } |
| 101 | |
| 102 | // 4. zero, otherwise |
| 103 | return { 0, nonCollapsedMargin }; |
| 104 | }; |
| 105 | |
| 106 | auto heightAndMargin = compute(); |
| 107 | LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow non-replaced -> height(" << heightAndMargin.height << "px) margin(" << heightAndMargin.nonCollapsedMargin.before << "px, " << heightAndMargin.nonCollapsedMargin.after << "px) -> layoutBox(" << &layoutBox << ")" ); |
| 108 | return heightAndMargin; |
| 109 | } |
| 110 | |
| 111 | WidthAndMargin BlockFormattingContext::Geometry::inFlowNonReplacedWidthAndMargin(const LayoutState& layoutState, const Box& layoutBox, UsedHorizontalValues usedValues) |
| 112 | { |
| 113 | ASSERT(layoutBox.isInFlow()); |
| 114 | |
| 115 | auto compute = [&]() { |
| 116 | |
| 117 | // 10.3.3 Block-level, non-replaced elements in normal flow |
| 118 | // |
| 119 | // The following constraints must hold among the used values of the other properties: |
| 120 | // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = width of containing block |
| 121 | // |
| 122 | // 1. If 'width' is not 'auto' and 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' |
| 123 | // (plus any of 'margin-left' or 'margin-right' that are not 'auto') is larger than the width of the containing block, then |
| 124 | // any 'auto' values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero. |
| 125 | // |
| 126 | // 2. If all of the above have a computed value other than 'auto', the values are said to be "over-constrained" and one of the used values will |
| 127 | // have to be different from its computed value. If the 'direction' property of the containing block has the value 'ltr', the specified value |
| 128 | // of 'margin-right' is ignored and the value is calculated so as to make the equality true. If the value of 'direction' is 'rtl', |
| 129 | // this happens to 'margin-left' instead. |
| 130 | // |
| 131 | // 3. If there is exactly one value specified as 'auto', its used value follows from the equality. |
| 132 | // |
| 133 | // 4. If 'width' is set to 'auto', any other 'auto' values become '0' and 'width' follows from the resulting equality. |
| 134 | // |
| 135 | // 5. If both 'margin-left' and 'margin-right' are 'auto', their used values are equal. This horizontally centers the element with respect to the |
| 136 | // edges of the containing block. |
| 137 | |
| 138 | auto& style = layoutBox.style(); |
| 139 | auto* containingBlock = layoutBox.containingBlock(); |
| 140 | auto containingBlockWidth = usedValues.containingBlockWidth.valueOr(0); |
| 141 | auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox); |
| 142 | |
| 143 | auto width = computedValueIfNotAuto(usedValues.width ? Length { usedValues.width.value(), Fixed } : style.logicalWidth(), containingBlockWidth); |
| 144 | auto computedHorizontalMargin = Geometry::computedHorizontalMargin(layoutBox, usedValues); |
| 145 | UsedHorizontalMargin usedHorizontalMargin; |
| 146 | auto borderLeft = displayBox.borderLeft(); |
| 147 | auto borderRight = displayBox.borderRight(); |
| 148 | auto paddingLeft = displayBox.paddingLeft().valueOr(0); |
| 149 | auto paddingRight = displayBox.paddingRight().valueOr(0); |
| 150 | auto contentWidth = [&] { |
| 151 | ASSERT(width); |
| 152 | return style.boxSizing() == BoxSizing::ContentBox ? *width : *width - (borderLeft + paddingLeft + paddingRight + borderRight); |
| 153 | }; |
| 154 | |
| 155 | // #1 |
| 156 | if (width) { |
| 157 | auto horizontalSpaceForMargin = containingBlockWidth - (computedHorizontalMargin.start.valueOr(0) + borderLeft + paddingLeft + contentWidth() + paddingRight + borderRight + computedHorizontalMargin.end.valueOr(0)); |
| 158 | if (horizontalSpaceForMargin < 0) |
| 159 | usedHorizontalMargin = { computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) }; |
| 160 | } |
| 161 | |
| 162 | // #2 |
| 163 | if (width && computedHorizontalMargin.start && computedHorizontalMargin.end) { |
| 164 | if (containingBlock->style().isLeftToRightDirection()) { |
| 165 | usedHorizontalMargin.start = *computedHorizontalMargin.start; |
| 166 | usedHorizontalMargin.end = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + contentWidth() + paddingRight + borderRight); |
| 167 | } else { |
| 168 | usedHorizontalMargin.end = *computedHorizontalMargin.end; |
| 169 | usedHorizontalMargin.start = containingBlockWidth - (borderLeft + paddingLeft + contentWidth() + paddingRight + borderRight + usedHorizontalMargin.end); |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | // #3 |
| 174 | if (!computedHorizontalMargin.start && width && computedHorizontalMargin.end) { |
| 175 | usedHorizontalMargin.end = *computedHorizontalMargin.end; |
| 176 | usedHorizontalMargin.start = containingBlockWidth - (borderLeft + paddingLeft + contentWidth() + paddingRight + borderRight + usedHorizontalMargin.end); |
| 177 | } else if (computedHorizontalMargin.start && !width && computedHorizontalMargin.end) { |
| 178 | usedHorizontalMargin = { *computedHorizontalMargin.start, *computedHorizontalMargin.end }; |
| 179 | width = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + paddingRight + borderRight + usedHorizontalMargin.end); |
| 180 | } else if (computedHorizontalMargin.start && width && !computedHorizontalMargin.end) { |
| 181 | usedHorizontalMargin.start = *computedHorizontalMargin.start; |
| 182 | usedHorizontalMargin.end = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + contentWidth() + paddingRight + borderRight); |
| 183 | } |
| 184 | |
| 185 | // #4 |
| 186 | if (!width) { |
| 187 | usedHorizontalMargin = { computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) }; |
| 188 | width = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + paddingRight + borderRight + usedHorizontalMargin.end); |
| 189 | } |
| 190 | |
| 191 | // #5 |
| 192 | if (!computedHorizontalMargin.start && !computedHorizontalMargin.end) { |
| 193 | auto horizontalSpaceForMargin = containingBlockWidth - (borderLeft + paddingLeft + contentWidth() + paddingRight + borderRight); |
| 194 | usedHorizontalMargin = { horizontalSpaceForMargin / 2, horizontalSpaceForMargin / 2 }; |
| 195 | } |
| 196 | |
| 197 | ASSERT(width); |
| 198 | |
| 199 | return WidthAndMargin { contentWidth(), usedHorizontalMargin, computedHorizontalMargin }; |
| 200 | }; |
| 201 | |
| 202 | auto widthAndMargin = compute(); |
| 203 | LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow non-replaced -> width(" << widthAndMargin.width << "px) margin(" << widthAndMargin.usedMargin.start << "px, " << widthAndMargin.usedMargin.end << "px) -> layoutBox(" << &layoutBox << ")" ); |
| 204 | return widthAndMargin; |
| 205 | } |
| 206 | |
| 207 | WidthAndMargin BlockFormattingContext::Geometry::inFlowReplacedWidthAndMargin(const LayoutState& layoutState, const Box& layoutBox, UsedHorizontalValues usedValues) |
| 208 | { |
| 209 | ASSERT(layoutBox.isInFlow() && layoutBox.replaced()); |
| 210 | |
| 211 | // 10.3.4 Block-level, replaced elements in normal flow |
| 212 | // |
| 213 | // 1. The used value of 'width' is determined as for inline replaced elements. |
| 214 | // 2. Then the rules for non-replaced block-level elements are applied to determine the margins. |
| 215 | |
| 216 | // #1 |
| 217 | usedValues.width = inlineReplacedWidthAndMargin(layoutState, layoutBox, usedValues).width; |
| 218 | // #2 |
| 219 | auto nonReplacedWidthAndMargin = inFlowNonReplacedWidthAndMargin(layoutState, layoutBox, usedValues); |
| 220 | |
| 221 | LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow replaced -> width(" << *usedValues.width << "px) margin(" << nonReplacedWidthAndMargin.usedMargin.start << "px, " << nonReplacedWidthAndMargin.usedMargin.end << "px) -> layoutBox(" << &layoutBox << ")" ); |
| 222 | return { *usedValues.width, nonReplacedWidthAndMargin.usedMargin, nonReplacedWidthAndMargin.computedMargin }; |
| 223 | } |
| 224 | |
| 225 | Point BlockFormattingContext::Geometry::staticPosition(const LayoutState& layoutState, const Box& layoutBox) |
| 226 | { |
| 227 | // https://www.w3.org/TR/CSS22/visuren.html#block-formatting |
| 228 | // In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block. |
| 229 | // The vertical distance between two sibling boxes is determined by the 'margin' properties. |
| 230 | // Vertical margins between adjacent block-level boxes in a block formatting context collapse. |
| 231 | // In a block formatting context, each box's left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch). |
| 232 | |
| 233 | LayoutUnit top; |
| 234 | auto& containingBlockDisplayBox = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()); |
| 235 | if (auto* previousInFlowSibling = layoutBox.previousInFlowSibling()) { |
| 236 | auto& previousInFlowDisplayBox = layoutState.displayBoxForLayoutBox(*previousInFlowSibling); |
| 237 | top = previousInFlowDisplayBox.bottom() + previousInFlowDisplayBox.marginAfter(); |
| 238 | } else |
| 239 | top = containingBlockDisplayBox.contentBoxTop(); |
| 240 | |
| 241 | auto left = containingBlockDisplayBox.contentBoxLeft() + layoutState.displayBoxForLayoutBox(layoutBox).marginStart(); |
| 242 | LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position] -> static -> top(" << top << "px) left(" << left << "px) layoutBox(" << &layoutBox << ")" ); |
| 243 | return { left, top }; |
| 244 | } |
| 245 | |
| 246 | HeightAndMargin BlockFormattingContext::Geometry::inFlowHeightAndMargin(const LayoutState& layoutState, const Box& layoutBox, UsedVerticalValues usedValues) |
| 247 | { |
| 248 | ASSERT(layoutBox.isInFlow()); |
| 249 | |
| 250 | // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block' |
| 251 | // replaced elements in normal flow and floating replaced elements |
| 252 | if (layoutBox.replaced()) |
| 253 | return inlineReplacedHeightAndMargin(layoutState, layoutBox, usedValues); |
| 254 | |
| 255 | HeightAndMargin heightAndMargin; |
| 256 | // TODO: Figure out the case for the document element. Let's just complicated-case it for now. |
| 257 | if (layoutBox.isOverflowVisible() && !layoutBox.isDocumentBox()) |
| 258 | heightAndMargin = inFlowNonReplacedHeightAndMargin(layoutState, layoutBox, usedValues); |
| 259 | else { |
| 260 | // 10.6.6 Complicated cases |
| 261 | // Block-level, non-replaced elements in normal flow when 'overflow' does not compute to 'visible' (except if the 'overflow' property's value has been propagated to the viewport). |
| 262 | auto usedHorizontalValues = UsedHorizontalValues { layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth() }; |
| 263 | heightAndMargin = complicatedCases(layoutState, layoutBox, usedValues, usedHorizontalValues); |
| 264 | } |
| 265 | |
| 266 | if (!Quirks::needsStretching(layoutState, layoutBox)) |
| 267 | return heightAndMargin; |
| 268 | |
| 269 | heightAndMargin = Quirks::stretchedInFlowHeight(layoutState, layoutBox, heightAndMargin); |
| 270 | |
| 271 | LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow non-replaced -> streched to viewport -> height(" << heightAndMargin.height << "px) margin(" << heightAndMargin.nonCollapsedMargin.before << "px, " << heightAndMargin.nonCollapsedMargin.after << "px) -> layoutBox(" << &layoutBox << ")" ); |
| 272 | return heightAndMargin; |
| 273 | } |
| 274 | |
| 275 | WidthAndMargin BlockFormattingContext::Geometry::inFlowWidthAndMargin(const LayoutState& layoutState, const Box& layoutBox, UsedHorizontalValues usedValues) |
| 276 | { |
| 277 | ASSERT(layoutBox.isInFlow()); |
| 278 | |
| 279 | if (!layoutBox.replaced()) |
| 280 | return inFlowNonReplacedWidthAndMargin(layoutState, layoutBox, usedValues); |
| 281 | return inFlowReplacedWidthAndMargin(layoutState, layoutBox, usedValues); |
| 282 | } |
| 283 | |
| 284 | bool BlockFormattingContext::Geometry::intrinsicWidthConstraintsNeedChildrenWidth(const Box& layoutBox) |
| 285 | { |
| 286 | if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowOrFloatingChild()) |
| 287 | return false; |
| 288 | return layoutBox.style().width().isAuto(); |
| 289 | } |
| 290 | |
| 291 | FormattingContext::IntrinsicWidthConstraints BlockFormattingContext::Geometry::intrinsicWidthConstraints(const LayoutState& layoutState, const Box& layoutBox) |
| 292 | { |
| 293 | auto computedIntrinsicWidthConstraints = [&]() -> IntrinsicWidthConstraints { |
| 294 | auto& style = layoutBox.style(); |
| 295 | if (auto width = fixedValue(style.logicalWidth())) |
| 296 | return { *width, *width }; |
| 297 | |
| 298 | // Minimum/maximum width can't be depending on the containing block's width. |
| 299 | if (!style.logicalWidth().isAuto()) |
| 300 | return { }; |
| 301 | |
| 302 | if (layoutBox.isReplaced()) { |
| 303 | auto& replaced = *layoutBox.replaced(); |
| 304 | if (replaced.hasIntrinsicWidth()) { |
| 305 | auto replacedWidth = replaced.intrinsicWidth(); |
| 306 | return { replacedWidth, replacedWidth }; |
| 307 | } |
| 308 | return { }; |
| 309 | } |
| 310 | |
| 311 | if (!is<Container>(layoutBox)) |
| 312 | return { }; |
| 313 | |
| 314 | auto intrinsicWidthConstraints = IntrinsicWidthConstraints { }; |
| 315 | for (auto& child : childrenOfType<Box>(downcast<Container>(layoutBox))) { |
| 316 | if (child.isOutOfFlowPositioned()) |
| 317 | continue; |
| 318 | const auto& formattingState = layoutState.formattingStateForBox(child); |
| 319 | ASSERT(formattingState.isBlockFormattingState()); |
| 320 | auto childIntrinsicWidthConstraints = formattingState.intrinsicWidthConstraints(child); |
| 321 | ASSERT(childIntrinsicWidthConstraints); |
| 322 | |
| 323 | auto& childStyle = child.style(); |
| 324 | auto marginBorderAndPadding = fixedValue(childStyle.marginStart()).valueOr(0) |
| 325 | + LayoutUnit { childStyle.borderLeftWidth() } |
| 326 | + fixedValue(childStyle.paddingLeft()).valueOr(0) |
| 327 | + fixedValue(childStyle.paddingRight()).valueOr(0) |
| 328 | + LayoutUnit { childStyle.borderRightWidth() } |
| 329 | + fixedValue(childStyle.marginEnd()).valueOr(0); |
| 330 | intrinsicWidthConstraints.minimum = std::max(intrinsicWidthConstraints.minimum, childIntrinsicWidthConstraints->minimum + marginBorderAndPadding); |
| 331 | intrinsicWidthConstraints.maximum = std::max(intrinsicWidthConstraints.maximum, childIntrinsicWidthConstraints->maximum + marginBorderAndPadding); |
| 332 | } |
| 333 | return intrinsicWidthConstraints; |
| 334 | }; |
| 335 | |
| 336 | return constrainByMinMaxWidth(layoutBox, computedIntrinsicWidthConstraints()); |
| 337 | } |
| 338 | |
| 339 | } |
| 340 | } |
| 341 | |
| 342 | #endif |
| 343 | |