zalan@apple.com | 4c4bae2 | 2018-04-23 23:08:47 +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. 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 "InlineFormattingContext.h" |
zalan@apple.com | ce6567c | 2018-04-25 22:54:01 +0000 | [diff] [blame] | 28 | |
| 29 | #if ENABLE(LAYOUT_FORMATTING_CONTEXT) |
| 30 | |
| 31 | #include "InlineFormattingState.h" |
zalan@apple.com | ae37bbb | 2018-10-19 16:41:58 +0000 | [diff] [blame] | 32 | #include "InlineLineBreaker.h" |
zalan@apple.com | 2bf3786 | 2019-05-25 20:14:09 +0000 | [diff] [blame] | 33 | #include "InlineTextItem.h" |
zalan@apple.com | d56d550 | 2018-04-27 15:40:16 +0000 | [diff] [blame] | 34 | #include "LayoutBox.h" |
zalan@apple.com | 5ac5709 | 2018-07-20 17:05:16 +0000 | [diff] [blame] | 35 | #include "LayoutContainer.h" |
zalan@apple.com | ac194b1 | 2019-09-20 13:36:00 +0000 | [diff] [blame] | 36 | #include "LayoutContext.h" |
antti@apple.com | 878f886 | 2018-12-08 00:26:34 +0000 | [diff] [blame] | 37 | #include "LayoutState.h" |
zalan@apple.com | 5ac5709 | 2018-07-20 17:05:16 +0000 | [diff] [blame] | 38 | #include "Logging.h" |
zalan@apple.com | 5901b78 | 2018-11-20 15:56:57 +0000 | [diff] [blame] | 39 | #include "Textutil.h" |
zalan@apple.com | ce6567c | 2018-04-25 22:54:01 +0000 | [diff] [blame] | 40 | #include <wtf/IsoMallocInlines.h> |
zalan@apple.com | 5ac5709 | 2018-07-20 17:05:16 +0000 | [diff] [blame] | 41 | #include <wtf/text/TextStream.h> |
zalan@apple.com | ce6567c | 2018-04-25 22:54:01 +0000 | [diff] [blame] | 42 | |
| 43 | namespace WebCore { |
| 44 | namespace Layout { |
| 45 | |
| 46 | WTF_MAKE_ISO_ALLOCATED_IMPL(InlineFormattingContext); |
| 47 | |
zalan@apple.com | 97eeb34 | 2019-09-13 19:15:48 +0000 | [diff] [blame] | 48 | InlineFormattingContext::InlineFormattingContext(const Container& formattingContextRoot, InlineFormattingState& formattingState) |
zalan@apple.com | 9892ac1 | 2018-10-31 13:50:05 +0000 | [diff] [blame] | 49 | : FormattingContext(formattingContextRoot, formattingState) |
zalan@apple.com | ce6567c | 2018-04-25 22:54:01 +0000 | [diff] [blame] | 50 | { |
| 51 | } |
| 52 | |
zalan@apple.com | a99fb22 | 2019-08-04 10:16:06 +0000 | [diff] [blame] | 53 | static inline const Box* nextInPreOrder(const Box& layoutBox, const Container& stayWithin) |
zalan@apple.com | 6bf87cf | 2019-02-05 16:01:30 +0000 | [diff] [blame] | 54 | { |
| 55 | const Box* nextInPreOrder = nullptr; |
| 56 | if (!layoutBox.establishesFormattingContext() && is<Container>(layoutBox) && downcast<Container>(layoutBox).hasInFlowOrFloatingChild()) |
| 57 | return downcast<Container>(layoutBox).firstInFlowOrFloatingChild(); |
| 58 | |
zalan@apple.com | a99fb22 | 2019-08-04 10:16:06 +0000 | [diff] [blame] | 59 | for (nextInPreOrder = &layoutBox; nextInPreOrder && nextInPreOrder != &stayWithin; nextInPreOrder = nextInPreOrder->parent()) { |
zalan@apple.com | 6bf87cf | 2019-02-05 16:01:30 +0000 | [diff] [blame] | 60 | if (auto* nextSibling = nextInPreOrder->nextInFlowOrFloatingSibling()) |
| 61 | return nextSibling; |
| 62 | } |
| 63 | return nullptr; |
| 64 | } |
| 65 | |
zalan@apple.com | 362e338 | 2019-09-13 16:40:40 +0000 | [diff] [blame] | 66 | void InlineFormattingContext::layoutInFlowContent() |
zalan@apple.com | ce6567c | 2018-04-25 22:54:01 +0000 | [diff] [blame] | 67 | { |
zalan@apple.com | 97eeb34 | 2019-09-13 19:15:48 +0000 | [diff] [blame] | 68 | if (!root().hasInFlowOrFloatingChild()) |
zalan@apple.com | 5ac5709 | 2018-07-20 17:05:16 +0000 | [diff] [blame] | 69 | return; |
| 70 | |
zalan@apple.com | 97eeb34 | 2019-09-13 19:15:48 +0000 | [diff] [blame] | 71 | LOG_WITH_STREAM(FormattingContextLayout, stream << "[Start] -> inline formatting context -> formatting root(" << &root() << ")"); |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 72 | auto& rootGeometry = geometryForBox(root()); |
| 73 | auto usedHorizontalValues = UsedHorizontalValues { UsedHorizontalValues::Constraints { rootGeometry } }; |
| 74 | auto usedVerticalValues = UsedVerticalValues { UsedVerticalValues::Constraints { rootGeometry } }; |
zalan@apple.com | 97eeb34 | 2019-09-13 19:15:48 +0000 | [diff] [blame] | 75 | auto* layoutBox = root().firstInFlowOrFloatingChild(); |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 76 | // 1. Visit each inline box and partially compute their geometry (margins, paddings and borders). |
| 77 | // 2. Collect the inline items (flatten the the layout tree) and place them on lines in bidirectional order. |
zalan@apple.com | 6bf87cf | 2019-02-05 16:01:30 +0000 | [diff] [blame] | 78 | while (layoutBox) { |
| 79 | if (layoutBox->establishesFormattingContext()) |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 80 | layoutFormattingContextRoot(*layoutBox, usedHorizontalValues, usedVerticalValues); |
zalan@apple.com | fa3d8f9 | 2019-09-23 17:21:22 +0000 | [diff] [blame] | 81 | else |
| 82 | computeHorizontalAndVerticalGeometry(*layoutBox, usedHorizontalValues, usedVerticalValues); |
zalan@apple.com | 97eeb34 | 2019-09-13 19:15:48 +0000 | [diff] [blame] | 83 | layoutBox = nextInPreOrder(*layoutBox, root()); |
zalan@apple.com | 6bf87cf | 2019-02-05 16:01:30 +0000 | [diff] [blame] | 84 | } |
zalan@apple.com | 5ac5709 | 2018-07-20 17:05:16 +0000 | [diff] [blame] | 85 | |
zalan@apple.com | 2bf3786 | 2019-05-25 20:14:09 +0000 | [diff] [blame] | 86 | // FIXME: This is such a waste when intrinsic width computation already collected the inline items. |
| 87 | formattingState().inlineItems().clear(); |
| 88 | formattingState().inlineRuns().clear(); |
| 89 | |
| 90 | collectInlineContent(); |
zalan@apple.com | f051779 | 2019-09-29 15:08:41 +0000 | [diff] [blame] | 91 | lineLayout(usedHorizontalValues); |
zalan@apple.com | 97eeb34 | 2019-09-13 19:15:48 +0000 | [diff] [blame] | 92 | LOG_WITH_STREAM(FormattingContextLayout, stream << "[End] -> inline formatting context -> formatting root(" << &root() << ")"); |
zalan@apple.com | ce6567c | 2018-04-25 22:54:01 +0000 | [diff] [blame] | 93 | } |
| 94 | |
zalan@apple.com | f051779 | 2019-09-29 15:08:41 +0000 | [diff] [blame] | 95 | void InlineFormattingContext::lineLayout(UsedHorizontalValues usedHorizontalValues) |
| 96 | { |
| 97 | auto& inlineItems = formattingState().inlineItems(); |
| 98 | auto lineLogicalTop = geometryForBox(root()).contentBoxTop(); |
| 99 | LineLayout::IndexAndRange currentInlineItem; |
| 100 | while (currentInlineItem.index < inlineItems.size()) { |
| 101 | auto lineConstraints = initialConstraintsForLine(usedHorizontalValues, lineLogicalTop); |
| 102 | auto lineInput = LineLayout::LineInput { lineConstraints, root().style().textAlign(), currentInlineItem, inlineItems }; |
| 103 | auto lineLayout = LineLayout { *this, lineInput }; |
| 104 | |
| 105 | auto lineContent = lineLayout.layout(); |
| 106 | setDisplayBoxesForLine(lineContent, usedHorizontalValues); |
| 107 | |
| 108 | if (lineContent.lastCommitted) { |
| 109 | currentInlineItem = { lineContent.lastCommitted->index + 1, WTF::nullopt }; |
| 110 | lineLogicalTop = lineContent.lineBox.logicalBottom(); |
| 111 | } else { |
| 112 | // Floats prevented us placing any content on the line. |
| 113 | ASSERT(lineInput.initialConstraints.lineIsConstrainedByFloat); |
| 114 | // Move the next line below the intrusive float. |
| 115 | auto floatingContext = FloatingContext { root(), *this, formattingState().floatingState() }; |
| 116 | auto floatConstraints = floatingContext.constraints({ lineLogicalTop }); |
| 117 | ASSERT(floatConstraints.left || floatConstraints.right); |
| 118 | static auto inifitePoint = PointInContextRoot::max(); |
| 119 | // In case of left and right constraints, we need to pick the one that's closer to the current line. |
| 120 | lineLogicalTop = std::min(floatConstraints.left.valueOr(inifitePoint).y, floatConstraints.right.valueOr(inifitePoint).y); |
| 121 | ASSERT(lineLogicalTop < inifitePoint.y); |
| 122 | } |
| 123 | } |
| 124 | } |
| 125 | |
zalan@apple.com | fa3d8f9 | 2019-09-23 17:21:22 +0000 | [diff] [blame] | 126 | void InlineFormattingContext::layoutFormattingContextRoot(const Box& formattingContextRoot, UsedHorizontalValues usedHorizontalValues, UsedVerticalValues usedVerticalValues) |
| 127 | { |
| 128 | ASSERT(formattingContextRoot.isFloatingPositioned() || formattingContextRoot.isInlineBlockBox()); |
| 129 | |
| 130 | computeBorderAndPadding(formattingContextRoot, usedHorizontalValues); |
| 131 | computeWidthAndMargin(formattingContextRoot, usedHorizontalValues); |
| 132 | // Swich over to the new formatting context (the one that the root creates). |
| 133 | if (is<Container>(formattingContextRoot)) { |
| 134 | auto& rootContainer = downcast<Container>(formattingContextRoot); |
| 135 | auto formattingContext = LayoutContext::createFormattingContext(rootContainer, layoutState()); |
| 136 | formattingContext->layoutInFlowContent(); |
| 137 | // Come back and finalize the root's height and margin. |
| 138 | computeHeightAndMargin(rootContainer, usedHorizontalValues, usedVerticalValues); |
| 139 | // Now that we computed the root's height, we can go back and layout the out-of-flow content. |
| 140 | formattingContext->layoutOutOfFlowContent(); |
| 141 | } else |
| 142 | computeHeightAndMargin(formattingContextRoot, usedHorizontalValues, usedVerticalValues); |
| 143 | } |
| 144 | |
| 145 | void InlineFormattingContext::computeHorizontalAndVerticalGeometry(const Box& layoutBox, UsedHorizontalValues usedHorizontalValues, UsedVerticalValues usedVerticalValues) |
| 146 | { |
| 147 | if (is<Container>(layoutBox)) { |
| 148 | // Inline containers (<span>) can't get sized/positioned yet. At this point we can only compute their margins, borders and paddings. |
| 149 | computeHorizontalMargin(layoutBox, usedHorizontalValues); |
| 150 | computeBorderAndPadding(layoutBox, usedHorizontalValues); |
| 151 | // Inline containers have 0 computed vertical margins. |
| 152 | formattingState().displayBox(layoutBox).setVerticalMargin({ { }, { } }); |
| 153 | return; |
| 154 | } |
| 155 | |
| 156 | if (layoutBox.isReplaced()) { |
| 157 | // Replaced elements (img, video) can be sized but not yet positioned. |
| 158 | computeBorderAndPadding(layoutBox, usedHorizontalValues); |
| 159 | computeWidthAndMargin(layoutBox, usedHorizontalValues); |
| 160 | computeHeightAndMargin(layoutBox, usedHorizontalValues, usedVerticalValues); |
| 161 | return; |
| 162 | } |
| 163 | |
| 164 | // These are actual text boxes. No margins, borders or paddings. |
| 165 | ASSERT(layoutBox.isAnonymous() || layoutBox.isLineBreakBox()); |
| 166 | auto& displayBox = formattingState().displayBox(layoutBox); |
| 167 | |
| 168 | displayBox.setVerticalMargin({ { }, { } }); |
| 169 | displayBox.setHorizontalMargin({ }); |
| 170 | displayBox.setBorder({ { }, { } }); |
| 171 | displayBox.setPadding({ }); |
| 172 | } |
| 173 | |
zalan@apple.com | 7aeac50 | 2019-08-30 17:02:31 +0000 | [diff] [blame] | 174 | FormattingContext::IntrinsicWidthConstraints InlineFormattingContext::computedIntrinsicWidthConstraints() |
zalan@apple.com | dee9586 | 2019-02-09 20:37:03 +0000 | [diff] [blame] | 175 | { |
zalan@apple.com | dee9586 | 2019-02-09 20:37:03 +0000 | [diff] [blame] | 176 | auto& layoutState = this->layoutState(); |
zalan@apple.com | a99fb22 | 2019-08-04 10:16:06 +0000 | [diff] [blame] | 177 | ASSERT(!formattingState().intrinsicWidthConstraints()); |
zalan@apple.com | 0950925 | 2019-08-03 16:09:47 +0000 | [diff] [blame] | 178 | |
zalan@apple.com | 97eeb34 | 2019-09-13 19:15:48 +0000 | [diff] [blame] | 179 | if (!root().hasInFlowOrFloatingChild()) { |
zalan@apple.com | 7aeac50 | 2019-08-30 17:02:31 +0000 | [diff] [blame] | 180 | auto constraints = geometry().constrainByMinMaxWidth(root(), { }); |
zalan@apple.com | a99fb22 | 2019-08-04 10:16:06 +0000 | [diff] [blame] | 181 | formattingState().setIntrinsicWidthConstraints(constraints); |
| 182 | return constraints; |
zalan@apple.com | 0950925 | 2019-08-03 16:09:47 +0000 | [diff] [blame] | 183 | } |
zalan@apple.com | dee9586 | 2019-02-09 20:37:03 +0000 | [diff] [blame] | 184 | |
zalan@apple.com | 25b5c77 | 2019-02-10 18:03:52 +0000 | [diff] [blame] | 185 | Vector<const Box*> formattingContextRootList; |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 186 | auto usedHorizontalValues = UsedHorizontalValues { UsedHorizontalValues::Constraints { { }, { } } }; |
zalan@apple.com | 97eeb34 | 2019-09-13 19:15:48 +0000 | [diff] [blame] | 187 | auto* layoutBox = root().firstInFlowOrFloatingChild(); |
zalan@apple.com | dee9586 | 2019-02-09 20:37:03 +0000 | [diff] [blame] | 188 | while (layoutBox) { |
zalan@apple.com | 694422c | 2019-02-12 15:34:36 +0000 | [diff] [blame] | 189 | if (layoutBox->establishesFormattingContext()) { |
zalan@apple.com | 25b5c77 | 2019-02-10 18:03:52 +0000 | [diff] [blame] | 190 | formattingContextRootList.append(layoutBox); |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 191 | computeIntrinsicWidthForFormattingRoot(*layoutBox, usedHorizontalValues); |
zalan@apple.com | 25b5c77 | 2019-02-10 18:03:52 +0000 | [diff] [blame] | 192 | } else if (layoutBox->isReplaced() || is<Container>(*layoutBox)) { |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 193 | computeBorderAndPadding(*layoutBox, usedHorizontalValues); |
zalan@apple.com | 25b5c77 | 2019-02-10 18:03:52 +0000 | [diff] [blame] | 194 | // inline-block and replaced. |
zalan@apple.com | a99fb22 | 2019-08-04 10:16:06 +0000 | [diff] [blame] | 195 | auto needsWidthComputation = layoutBox->isReplaced(); |
zalan@apple.com | 25b5c77 | 2019-02-10 18:03:52 +0000 | [diff] [blame] | 196 | if (needsWidthComputation) |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 197 | computeWidthAndMargin(*layoutBox, usedHorizontalValues); |
zalan@apple.com | 27ca52c | 2019-02-10 16:53:41 +0000 | [diff] [blame] | 198 | else { |
| 199 | // Simple inline container with no intrinsic width <span>. |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 200 | computeHorizontalMargin(*layoutBox, usedHorizontalValues); |
zalan@apple.com | 27ca52c | 2019-02-10 16:53:41 +0000 | [diff] [blame] | 201 | } |
| 202 | } |
zalan@apple.com | 97eeb34 | 2019-09-13 19:15:48 +0000 | [diff] [blame] | 203 | layoutBox = nextInPreOrder(*layoutBox, root()); |
zalan@apple.com | dee9586 | 2019-02-09 20:37:03 +0000 | [diff] [blame] | 204 | } |
| 205 | |
zalan@apple.com | 2bf3786 | 2019-05-25 20:14:09 +0000 | [diff] [blame] | 206 | collectInlineContent(); |
zalan@apple.com | dee9586 | 2019-02-09 20:37:03 +0000 | [diff] [blame] | 207 | |
| 208 | auto maximumLineWidth = [&](auto availableWidth) { |
zalan@apple.com | 25b5c77 | 2019-02-10 18:03:52 +0000 | [diff] [blame] | 209 | // Switch to the min/max formatting root width values before formatting the lines. |
| 210 | for (auto* formattingRoot : formattingContextRootList) { |
zalan@apple.com | a99fb22 | 2019-08-04 10:16:06 +0000 | [diff] [blame] | 211 | auto intrinsicWidths = layoutState.formattingStateForBox(*formattingRoot).intrinsicWidthConstraintsForBox(*formattingRoot); |
zalan@apple.com | 3b39c36 | 2019-09-13 15:42:28 +0000 | [diff] [blame] | 212 | auto& displayBox = formattingState().displayBox(*formattingRoot); |
zalan@apple.com | a99fb22 | 2019-08-04 10:16:06 +0000 | [diff] [blame] | 213 | auto contentWidth = (availableWidth ? intrinsicWidths->maximum : intrinsicWidths->minimum) - displayBox.horizontalMarginBorderAndPadding(); |
| 214 | displayBox.setContentBoxWidth(contentWidth); |
zalan@apple.com | 25b5c77 | 2019-02-10 18:03:52 +0000 | [diff] [blame] | 215 | } |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 216 | auto usedHorizontalValues = UsedHorizontalValues { UsedHorizontalValues::Constraints { { }, availableWidth } }; |
zalan@apple.com | f051779 | 2019-09-29 15:08:41 +0000 | [diff] [blame] | 217 | return computedIntrinsicWidthForConstraint(usedHorizontalValues); |
zalan@apple.com | dee9586 | 2019-02-09 20:37:03 +0000 | [diff] [blame] | 218 | }; |
| 219 | |
zalan@apple.com | 97eeb34 | 2019-09-13 19:15:48 +0000 | [diff] [blame] | 220 | auto constraints = geometry().constrainByMinMaxWidth(root(), { maximumLineWidth(0), maximumLineWidth(LayoutUnit::max()) }); |
zalan@apple.com | a99fb22 | 2019-08-04 10:16:06 +0000 | [diff] [blame] | 221 | formattingState().setIntrinsicWidthConstraints(constraints); |
| 222 | return constraints; |
zalan@apple.com | dee9586 | 2019-02-09 20:37:03 +0000 | [diff] [blame] | 223 | } |
| 224 | |
zalan@apple.com | f051779 | 2019-09-29 15:08:41 +0000 | [diff] [blame] | 225 | LayoutUnit InlineFormattingContext::computedIntrinsicWidthForConstraint(UsedHorizontalValues usedHorizontalValues) const |
| 226 | { |
| 227 | auto& inlineItems = formattingState().inlineItems(); |
| 228 | LayoutUnit maximumLineWidth; |
| 229 | LineLayout::IndexAndRange currentInlineItem; |
| 230 | while (currentInlineItem.index < inlineItems.size()) { |
| 231 | // Only the horiztonal available width is constrained when computing intrinsic width. |
| 232 | auto initialLineConstraints = Line::InitialConstraints { { }, usedHorizontalValues.constraints.width, false, { } }; |
| 233 | auto lineInput = LineLayout::LineInput { initialLineConstraints, currentInlineItem, inlineItems }; |
| 234 | |
| 235 | auto lineContent = LineLayout(*this, lineInput).layout(); |
| 236 | |
| 237 | currentInlineItem = { lineContent.lastCommitted->index + 1, { } }; |
| 238 | LayoutUnit floatsWidth; |
| 239 | for (auto& floatItem : lineContent.floats) |
| 240 | floatsWidth += geometryForBox(floatItem->layoutBox()).marginBoxWidth(); |
| 241 | maximumLineWidth = std::max(maximumLineWidth, floatsWidth + lineContent.lineBox.logicalWidth()); |
| 242 | } |
| 243 | return maximumLineWidth; |
| 244 | } |
| 245 | |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 246 | void InlineFormattingContext::computeIntrinsicWidthForFormattingRoot(const Box& formattingRoot, UsedHorizontalValues usedHorizontalValues) |
zalan@apple.com | 25b5c77 | 2019-02-10 18:03:52 +0000 | [diff] [blame] | 247 | { |
zalan@apple.com | a99fb22 | 2019-08-04 10:16:06 +0000 | [diff] [blame] | 248 | ASSERT(formattingRoot.establishesFormattingContext()); |
zalan@apple.com | 694422c | 2019-02-12 15:34:36 +0000 | [diff] [blame] | 249 | |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 250 | computeBorderAndPadding(formattingRoot, usedHorizontalValues); |
| 251 | computeHorizontalMargin(formattingRoot, usedHorizontalValues); |
zalan@apple.com | 694422c | 2019-02-12 15:34:36 +0000 | [diff] [blame] | 252 | |
zalan@apple.com | 97eeb34 | 2019-09-13 19:15:48 +0000 | [diff] [blame] | 253 | auto constraints = IntrinsicWidthConstraints { }; |
zalan@apple.com | 7aeac50 | 2019-08-30 17:02:31 +0000 | [diff] [blame] | 254 | if (auto fixedWidth = geometry().fixedValue(formattingRoot.style().logicalWidth())) |
zalan@apple.com | a99fb22 | 2019-08-04 10:16:06 +0000 | [diff] [blame] | 255 | constraints = { *fixedWidth, *fixedWidth }; |
zalan@apple.com | 97eeb34 | 2019-09-13 19:15:48 +0000 | [diff] [blame] | 256 | else if (is<Container>(formattingRoot)) |
zalan@apple.com | ac194b1 | 2019-09-20 13:36:00 +0000 | [diff] [blame] | 257 | constraints = LayoutContext::createFormattingContext(downcast<Container>(formattingRoot), layoutState())->computedIntrinsicWidthConstraints(); |
zalan@apple.com | 7aeac50 | 2019-08-30 17:02:31 +0000 | [diff] [blame] | 258 | constraints = geometry().constrainByMinMaxWidth(formattingRoot, constraints); |
zalan@apple.com | 3b39c36 | 2019-09-13 15:42:28 +0000 | [diff] [blame] | 259 | constraints.expand(geometryForBox(formattingRoot).horizontalMarginBorderAndPadding()); |
zalan@apple.com | a99fb22 | 2019-08-04 10:16:06 +0000 | [diff] [blame] | 260 | formattingState().setIntrinsicWidthConstraintsForBox(formattingRoot, constraints); |
zalan@apple.com | 25b5c77 | 2019-02-10 18:03:52 +0000 | [diff] [blame] | 261 | } |
| 262 | |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 263 | void InlineFormattingContext::computeHorizontalMargin(const Box& layoutBox, UsedHorizontalValues usedHorizontalValues) |
zalan@apple.com | 6bf87cf | 2019-02-05 16:01:30 +0000 | [diff] [blame] | 264 | { |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 265 | auto computedHorizontalMargin = geometry().computedHorizontalMargin(layoutBox, usedHorizontalValues); |
zalan@apple.com | 3b39c36 | 2019-09-13 15:42:28 +0000 | [diff] [blame] | 266 | auto& displayBox = formattingState().displayBox(layoutBox); |
zalan@apple.com | 6bf87cf | 2019-02-05 16:01:30 +0000 | [diff] [blame] | 267 | displayBox.setHorizontalComputedMargin(computedHorizontalMargin); |
| 268 | displayBox.setHorizontalMargin({ computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) }); |
| 269 | } |
| 270 | |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 271 | void InlineFormattingContext::computeWidthAndMargin(const Box& layoutBox, UsedHorizontalValues usedHorizontalValues) |
zalan@apple.com | ae37bbb | 2018-10-19 16:41:58 +0000 | [diff] [blame] | 272 | { |
zalan@apple.com | 4add905 | 2018-11-02 14:23:54 +0000 | [diff] [blame] | 273 | WidthAndMargin widthAndMargin; |
| 274 | if (layoutBox.isFloatingPositioned()) |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 275 | widthAndMargin = geometry().floatingWidthAndMargin(layoutBox, usedHorizontalValues); |
zalan@apple.com | 4add905 | 2018-11-02 14:23:54 +0000 | [diff] [blame] | 276 | else if (layoutBox.isInlineBlockBox()) |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 277 | widthAndMargin = geometry().inlineBlockWidthAndMargin(layoutBox, usedHorizontalValues); |
zalan@apple.com | 4add905 | 2018-11-02 14:23:54 +0000 | [diff] [blame] | 278 | else if (layoutBox.replaced()) |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 279 | widthAndMargin = geometry().inlineReplacedWidthAndMargin(layoutBox, usedHorizontalValues); |
zalan@apple.com | 4add905 | 2018-11-02 14:23:54 +0000 | [diff] [blame] | 280 | else |
| 281 | ASSERT_NOT_REACHED(); |
zalan@apple.com | ae37bbb | 2018-10-19 16:41:58 +0000 | [diff] [blame] | 282 | |
zalan@apple.com | 3b39c36 | 2019-09-13 15:42:28 +0000 | [diff] [blame] | 283 | auto& displayBox = formattingState().displayBox(layoutBox); |
zalan@apple.com | 9a4acd0 | 2018-10-26 14:36:30 +0000 | [diff] [blame] | 284 | displayBox.setContentBoxWidth(widthAndMargin.width); |
zalan@apple.com | 9707706 | 2019-01-04 15:21:45 +0000 | [diff] [blame] | 285 | displayBox.setHorizontalMargin(widthAndMargin.usedMargin); |
| 286 | displayBox.setHorizontalComputedMargin(widthAndMargin.computedMargin); |
zalan@apple.com | 4add905 | 2018-11-02 14:23:54 +0000 | [diff] [blame] | 287 | } |
zalan@apple.com | ae37bbb | 2018-10-19 16:41:58 +0000 | [diff] [blame] | 288 | |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 289 | void InlineFormattingContext::computeHeightAndMargin(const Box& layoutBox, UsedHorizontalValues usedHorizontalValues, UsedVerticalValues usedVerticalValues) |
zalan@apple.com | 4add905 | 2018-11-02 14:23:54 +0000 | [diff] [blame] | 290 | { |
zalan@apple.com | 4add905 | 2018-11-02 14:23:54 +0000 | [diff] [blame] | 291 | HeightAndMargin heightAndMargin; |
| 292 | if (layoutBox.isFloatingPositioned()) |
zalan@apple.com | 8237af0 | 2019-09-18 21:48:03 +0000 | [diff] [blame] | 293 | heightAndMargin = geometry().floatingHeightAndMargin(layoutBox, usedHorizontalValues, usedVerticalValues); |
zalan@apple.com | 4add905 | 2018-11-02 14:23:54 +0000 | [diff] [blame] | 294 | else if (layoutBox.isInlineBlockBox()) |
zalan@apple.com | 8237af0 | 2019-09-18 21:48:03 +0000 | [diff] [blame] | 295 | heightAndMargin = geometry().inlineBlockHeightAndMargin(layoutBox, usedHorizontalValues, usedVerticalValues); |
zalan@apple.com | 4add905 | 2018-11-02 14:23:54 +0000 | [diff] [blame] | 296 | else if (layoutBox.replaced()) |
zalan@apple.com | 8237af0 | 2019-09-18 21:48:03 +0000 | [diff] [blame] | 297 | heightAndMargin = geometry().inlineReplacedHeightAndMargin(layoutBox, usedHorizontalValues, usedVerticalValues); |
zalan@apple.com | 4add905 | 2018-11-02 14:23:54 +0000 | [diff] [blame] | 298 | else |
| 299 | ASSERT_NOT_REACHED(); |
| 300 | |
zalan@apple.com | 3b39c36 | 2019-09-13 15:42:28 +0000 | [diff] [blame] | 301 | auto& displayBox = formattingState().displayBox(layoutBox); |
zalan@apple.com | 9a4acd0 | 2018-10-26 14:36:30 +0000 | [diff] [blame] | 302 | displayBox.setContentBoxHeight(heightAndMargin.height); |
zalan@apple.com | a33797c | 2019-01-07 16:18:52 +0000 | [diff] [blame] | 303 | displayBox.setVerticalMargin({ heightAndMargin.nonCollapsedMargin, { } }); |
zalan@apple.com | ae37bbb | 2018-10-19 16:41:58 +0000 | [diff] [blame] | 304 | } |
| 305 | |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 306 | void InlineFormattingContext::computeWidthAndHeightForReplacedInlineBox(const Box& layoutBox, UsedHorizontalValues usedHorizontalValues, UsedVerticalValues usedVerticalValues) |
zalan@apple.com | 4add905 | 2018-11-02 14:23:54 +0000 | [diff] [blame] | 307 | { |
| 308 | ASSERT(!layoutBox.isContainer()); |
| 309 | ASSERT(!layoutBox.establishesFormattingContext()); |
| 310 | ASSERT(layoutBox.replaced()); |
| 311 | |
zalan@apple.com | dfa3372 | 2019-09-16 16:56:35 +0000 | [diff] [blame] | 312 | computeBorderAndPadding(layoutBox, usedHorizontalValues); |
| 313 | computeWidthAndMargin(layoutBox, usedHorizontalValues); |
zalan@apple.com | f6eb9b0 | 2019-09-23 16:14:21 +0000 | [diff] [blame] | 314 | computeHeightAndMargin(layoutBox, usedHorizontalValues, usedVerticalValues); |
zalan@apple.com | 4add905 | 2018-11-02 14:23:54 +0000 | [diff] [blame] | 315 | } |
| 316 | |
zalan@apple.com | 9ed00c0 | 2019-09-18 23:33:08 +0000 | [diff] [blame] | 317 | void InlineFormattingContext::collectInlineContent() |
zalan@apple.com | 3ec85f3 | 2018-11-14 15:31:17 +0000 | [diff] [blame] | 318 | { |
zalan@apple.com | 2bf3786 | 2019-05-25 20:14:09 +0000 | [diff] [blame] | 319 | // Traverse the tree and create inline items out of containers and leaf nodes. This essentially turns the tree inline structure into a flat one. |
| 320 | // <span>text<span></span><img></span> -> [ContainerStart][InlineBox][ContainerStart][ContainerEnd][InlineBox][ContainerEnd] |
| 321 | auto& formattingState = this->formattingState(); |
zalan@apple.com | 57261ba | 2019-02-04 15:49:24 +0000 | [diff] [blame] | 322 | LayoutQueue layoutQueue; |
zalan@apple.com | 97eeb34 | 2019-09-13 19:15:48 +0000 | [diff] [blame] | 323 | if (root().hasInFlowOrFloatingChild()) |
| 324 | layoutQueue.append(root().firstInFlowOrFloatingChild()); |
zalan@apple.com | 57261ba | 2019-02-04 15:49:24 +0000 | [diff] [blame] | 325 | while (!layoutQueue.isEmpty()) { |
zalan@apple.com | 2bf3786 | 2019-05-25 20:14:09 +0000 | [diff] [blame] | 326 | auto treatAsInlineContainer = [](auto& layoutBox) { |
| 327 | return is<Container>(layoutBox) && !layoutBox.establishesFormattingContext(); |
| 328 | }; |
zalan@apple.com | 57261ba | 2019-02-04 15:49:24 +0000 | [diff] [blame] | 329 | while (true) { |
| 330 | auto& layoutBox = *layoutQueue.last(); |
zalan@apple.com | 2bf3786 | 2019-05-25 20:14:09 +0000 | [diff] [blame] | 331 | if (!treatAsInlineContainer(layoutBox)) |
zalan@apple.com | 57261ba | 2019-02-04 15:49:24 +0000 | [diff] [blame] | 332 | break; |
zalan@apple.com | 2bf3786 | 2019-05-25 20:14:09 +0000 | [diff] [blame] | 333 | // This is the start of an inline container (e.g. <span>). |
ysuzuki@apple.com | 1d8e24d | 2019-08-19 06:59:40 +0000 | [diff] [blame] | 334 | formattingState.addInlineItem(makeUnique<InlineItem>(layoutBox, InlineItem::Type::ContainerStart)); |
zalan@apple.com | 57261ba | 2019-02-04 15:49:24 +0000 | [diff] [blame] | 335 | auto& container = downcast<Container>(layoutBox); |
zalan@apple.com | 57261ba | 2019-02-04 15:49:24 +0000 | [diff] [blame] | 336 | if (!container.hasInFlowOrFloatingChild()) |
| 337 | break; |
| 338 | layoutQueue.append(container.firstInFlowOrFloatingChild()); |
| 339 | } |
| 340 | |
| 341 | while (!layoutQueue.isEmpty()) { |
| 342 | auto& layoutBox = *layoutQueue.takeLast(); |
zalan@apple.com | 2bf3786 | 2019-05-25 20:14:09 +0000 | [diff] [blame] | 343 | // This is the end of an inline container (e.g. </span>). |
| 344 | if (treatAsInlineContainer(layoutBox)) |
ysuzuki@apple.com | 1d8e24d | 2019-08-19 06:59:40 +0000 | [diff] [blame] | 345 | formattingState.addInlineItem(makeUnique<InlineItem>(layoutBox, InlineItem::Type::ContainerEnd)); |
zalan@apple.com | 0950925 | 2019-08-03 16:09:47 +0000 | [diff] [blame] | 346 | else if (layoutBox.isLineBreakBox()) |
ysuzuki@apple.com | 1d8e24d | 2019-08-19 06:59:40 +0000 | [diff] [blame] | 347 | formattingState.addInlineItem(makeUnique<InlineItem>(layoutBox, InlineItem::Type::HardLineBreak)); |
zalan@apple.com | 2bf3786 | 2019-05-25 20:14:09 +0000 | [diff] [blame] | 348 | else if (layoutBox.isFloatingPositioned()) |
ysuzuki@apple.com | 1d8e24d | 2019-08-19 06:59:40 +0000 | [diff] [blame] | 349 | formattingState.addInlineItem(makeUnique<InlineItem>(layoutBox, InlineItem::Type::Float)); |
zalan@apple.com | 2bf3786 | 2019-05-25 20:14:09 +0000 | [diff] [blame] | 350 | else { |
zalan@apple.com | 0950925 | 2019-08-03 16:09:47 +0000 | [diff] [blame] | 351 | ASSERT(layoutBox.isInlineLevelBox()); |
| 352 | if (layoutBox.hasTextContent()) |
| 353 | InlineTextItem::createAndAppendTextItems(formattingState.inlineItems(), layoutBox); |
zalan@apple.com | 2bf3786 | 2019-05-25 20:14:09 +0000 | [diff] [blame] | 354 | else |
ysuzuki@apple.com | 1d8e24d | 2019-08-19 06:59:40 +0000 | [diff] [blame] | 355 | formattingState.addInlineItem(makeUnique<InlineItem>(layoutBox, InlineItem::Type::Box)); |
zalan@apple.com | 57261ba | 2019-02-04 15:49:24 +0000 | [diff] [blame] | 356 | } |
| 357 | |
| 358 | if (auto* nextSibling = layoutBox.nextInFlowOrFloatingSibling()) { |
| 359 | layoutQueue.append(nextSibling); |
| 360 | break; |
| 361 | } |
| 362 | } |
| 363 | } |
zalan@apple.com | 4add905 | 2018-11-02 14:23:54 +0000 | [diff] [blame] | 364 | } |
| 365 | |
zalan@apple.com | f051779 | 2019-09-29 15:08:41 +0000 | [diff] [blame] | 366 | Line::InitialConstraints InlineFormattingContext::initialConstraintsForLine(UsedHorizontalValues usedHorizontalValues, const LayoutUnit lineLogicalTop) |
| 367 | { |
| 368 | auto lineLogicalLeft = geometryForBox(root()).contentBoxLeft(); |
| 369 | auto availableWidth = usedHorizontalValues.constraints.width; |
| 370 | auto lineIsConstrainedByFloat = false; |
| 371 | |
| 372 | auto floatingContext = FloatingContext { root(), *this, formattingState().floatingState() }; |
| 373 | // Check for intruding floats and adjust logical left/available width for this line accordingly. |
| 374 | if (!floatingContext.isEmpty()) { |
| 375 | auto floatConstraints = floatingContext.constraints({ lineLogicalTop }); |
| 376 | // Check if these constraints actually put limitation on the line. |
| 377 | if (floatConstraints.left && floatConstraints.left->x <= lineLogicalLeft) |
| 378 | floatConstraints.left = { }; |
| 379 | |
| 380 | auto lineLogicalRight = geometryForBox(root()).contentBoxRight(); |
| 381 | if (floatConstraints.right && floatConstraints.right->x >= lineLogicalRight) |
| 382 | floatConstraints.right = { }; |
| 383 | |
| 384 | lineIsConstrainedByFloat = floatConstraints.left || floatConstraints.right; |
| 385 | |
| 386 | if (floatConstraints.left && floatConstraints.right) { |
| 387 | ASSERT(floatConstraints.left->x <= floatConstraints.right->x); |
| 388 | availableWidth = floatConstraints.right->x - floatConstraints.left->x; |
| 389 | lineLogicalLeft = floatConstraints.left->x; |
| 390 | } else if (floatConstraints.left) { |
| 391 | ASSERT(floatConstraints.left->x >= lineLogicalLeft); |
| 392 | availableWidth -= (floatConstraints.left->x - lineLogicalLeft); |
| 393 | lineLogicalLeft = floatConstraints.left->x; |
| 394 | } else if (floatConstraints.right) { |
| 395 | ASSERT(floatConstraints.right->x >= lineLogicalLeft); |
| 396 | availableWidth = floatConstraints.right->x - lineLogicalLeft; |
| 397 | } |
| 398 | } |
| 399 | return Line::InitialConstraints { { lineLogicalLeft, lineLogicalTop }, availableWidth, lineIsConstrainedByFloat, quirks().lineHeightConstraints(root()) }; |
| 400 | } |
| 401 | |
| 402 | void InlineFormattingContext::setDisplayBoxesForLine(const LineLayout::LineContent& lineContent, UsedHorizontalValues usedHorizontalValues) |
| 403 | { |
| 404 | auto& formattingState = this->formattingState(); |
| 405 | |
| 406 | if (!lineContent.floats.isEmpty()) { |
| 407 | auto floatingContext = FloatingContext { root(), *this, formattingState.floatingState() }; |
| 408 | // Move floats to their final position. |
| 409 | for (const auto& floatItem : lineContent.floats) { |
| 410 | auto& floatBox = floatItem->layoutBox(); |
| 411 | auto& displayBox = formattingState.displayBox(floatBox); |
| 412 | // Set static position first. |
| 413 | auto& lineBox = lineContent.lineBox; |
| 414 | displayBox.setTopLeft({ lineBox.logicalLeft(), lineBox.logicalTop() }); |
| 415 | // Float it. |
| 416 | displayBox.setTopLeft(floatingContext.positionForFloat(floatBox)); |
| 417 | floatingContext.append(floatBox); |
| 418 | } |
| 419 | } |
| 420 | |
| 421 | // Add final display runs to state. |
| 422 | for (auto& lineRun : lineContent.runList) { |
| 423 | // Inline level containers (<span>) don't generate inline runs. |
| 424 | if (lineRun->isContainerStart() || lineRun->isContainerEnd()) |
| 425 | continue; |
| 426 | // Collapsed line runs don't generate display runs. |
| 427 | if (lineRun->isVisuallyEmpty()) |
| 428 | continue; |
| 429 | formattingState.addInlineRun(lineRun->displayRun()); |
| 430 | } |
| 431 | |
| 432 | // Compute box final geometry. |
| 433 | auto& lineRuns = lineContent.runList; |
| 434 | for (unsigned index = 0; index < lineRuns.size(); ++index) { |
| 435 | auto& lineRun = lineRuns.at(index); |
| 436 | auto& logicalRect = lineRun->logicalRect(); |
| 437 | auto& layoutBox = lineRun->layoutBox(); |
| 438 | auto& displayBox = formattingState.displayBox(layoutBox); |
| 439 | |
| 440 | if (lineRun->isLineBreak()) { |
| 441 | displayBox.setTopLeft(logicalRect.topLeft()); |
| 442 | displayBox.setContentBoxWidth(logicalRect.width()); |
| 443 | displayBox.setContentBoxHeight(logicalRect.height()); |
| 444 | continue; |
| 445 | } |
| 446 | |
| 447 | // Inline level box (replaced or inline-block) |
| 448 | if (lineRun->isBox()) { |
| 449 | auto topLeft = logicalRect.topLeft(); |
| 450 | if (layoutBox.isInFlowPositioned()) |
| 451 | topLeft += geometry().inFlowPositionedPositionOffset(layoutBox, usedHorizontalValues); |
| 452 | displayBox.setTopLeft(topLeft); |
| 453 | continue; |
| 454 | } |
| 455 | |
| 456 | // Inline level container start (<span>) |
| 457 | if (lineRun->isContainerStart()) { |
| 458 | displayBox.setTopLeft(logicalRect.topLeft()); |
| 459 | continue; |
| 460 | } |
| 461 | |
| 462 | // Inline level container end (</span>) |
| 463 | if (lineRun->isContainerEnd()) { |
| 464 | if (layoutBox.isInFlowPositioned()) { |
| 465 | auto inflowOffset = geometry().inFlowPositionedPositionOffset(layoutBox, usedHorizontalValues); |
| 466 | displayBox.moveHorizontally(inflowOffset.width()); |
| 467 | displayBox.moveVertically(inflowOffset.height()); |
| 468 | } |
| 469 | auto marginBoxWidth = logicalRect.left() - displayBox.left(); |
| 470 | auto contentBoxWidth = marginBoxWidth - (displayBox.marginStart() + displayBox.borderLeft() + displayBox.paddingLeft().valueOr(0)); |
| 471 | // FIXME fix it for multiline. |
| 472 | displayBox.setContentBoxWidth(contentBoxWidth); |
| 473 | displayBox.setContentBoxHeight(logicalRect.height()); |
| 474 | continue; |
| 475 | } |
| 476 | |
| 477 | if (lineRun->isText()) { |
| 478 | const Line::Run* previousLineRun = !index ? nullptr : lineRuns[index - 1].get(); |
| 479 | // FIXME take content breaking into account when part of the layout box is on the previous line. |
| 480 | auto firstInlineRunForLayoutBox = !previousLineRun || &previousLineRun->layoutBox() != &layoutBox; |
| 481 | auto logicalWidth = lineRun->isVisuallyEmpty() ? LayoutUnit() : logicalRect.width(); |
| 482 | if (firstInlineRunForLayoutBox) { |
| 483 | // Setup display box for the associated layout box. |
| 484 | displayBox.setTopLeft(logicalRect.topLeft()); |
| 485 | displayBox.setContentBoxWidth(logicalWidth); |
| 486 | displayBox.setContentBoxHeight(logicalRect.height()); |
| 487 | } else { |
| 488 | // FIXME fix it for multirun/multiline. |
| 489 | displayBox.setContentBoxWidth(displayBox.contentBoxWidth() + logicalWidth); |
| 490 | } |
| 491 | continue; |
| 492 | } |
| 493 | ASSERT_NOT_REACHED(); |
| 494 | } |
| 495 | formattingState.addLineBox(lineContent.lineBox); |
| 496 | } |
| 497 | |
zalan@apple.com | ce6567c | 2018-04-25 22:54:01 +0000 | [diff] [blame] | 498 | } |
| 499 | } |
| 500 | |
| 501 | #endif |