Diff
Modified: trunk/Source/WebCore/ChangeLog (266679 => 266680)
--- trunk/Source/WebCore/ChangeLog 2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/ChangeLog 2020-09-06 15:59:35 UTC (rev 266680)
@@ -1,3 +1,44 @@
+2020-09-06 Zalan Bujtas <[email protected]>
+
+ [LFC][IFC] Move Line handing to LineBuilder
+ https://bugs.webkit.org/show_bug.cgi?id=216200
+
+ Reviewed by Antti Koivisto.
+
+ 1. LineBuilder owns Line now.
+ 2. LineBuilder can build line for both layout and intrinsic width computation (now these are two distinct code paths).
+ 3. LineBuilder also constructs the LineBox when needed (This was previously in InlineFormattingContext::lineLayout).
+
+ * layout/inlineformatting/InlineFormattingContext.cpp:
+ (WebCore::Layout::InlineFormattingContext::lineLayout):
+ (WebCore::Layout::InlineFormattingContext::computedIntrinsicWidthForConstraint const):
+ (WebCore::Layout::InlineFormattingContext::setDisplayBoxesForLine):
+ (WebCore::Layout::InlineFormattingContext::constraintsForLine): Deleted.
+ * layout/inlineformatting/InlineFormattingContext.h:
+ * layout/inlineformatting/InlineFormattingContextGeometry.cpp:
+ (WebCore::Layout::InlineFormattingContext::Geometry::computedTextIndent const): Deleted.
+ * layout/inlineformatting/InlineLineBox.cpp:
+ (WebCore::Layout::LineBox::formattingContext const):
+ (WebCore::Layout::LineBox::root const):
+ (WebCore::Layout::LineBox::layoutState const):
+ * layout/inlineformatting/InlineLineBox.h:
+ (WebCore::Layout::LineBox::contentLogicalWidth const):
+ (WebCore::Layout::LineBox::formattingContext const): Deleted.
+ (WebCore::Layout::LineBox::root const): Deleted.
+ (WebCore::Layout::LineBox::layoutState const): Deleted.
+ * layout/inlineformatting/InlineLineBuilder.cpp:
+ (WebCore::Layout::LineBuilder::LineBuilder):
+ (WebCore::Layout::LineBuilder::layoutInlineContent):
+ (WebCore::Layout::LineBuilder::initialize):
+ (WebCore::Layout::LineBuilder::close):
+ (WebCore::Layout::LineBuilder::constraintsForLine):
+ (WebCore::Layout::LineBuilder::commitFloats):
+ (WebCore::Layout::LineBuilder::handleFloatsAndInlineContent):
+ (WebCore::Layout::LineBuilder::commitPartialContent):
+ (WebCore::Layout::LineBuilder::rebuildLine):
+ (WebCore::Layout::LineBuilder::layoutState const):
+ * layout/inlineformatting/InlineLineBuilder.h:
+
2020-09-05 Sam Weinig <[email protected]>
[WebIDL] Add support for interface mixins
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp (266679 => 266680)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp 2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp 2020-09-06 15:59:35 UTC (rev 266680)
@@ -140,42 +140,22 @@
Optional<unsigned> overflowContentLength;
};
Optional<PreviousLine> previousLine;
- auto line = Line { *this };
- auto lineBuilder = LineBuilder { *this, root(), inlineItems };
+ auto floatingContext = FloatingContext { root(), *this, formattingState().floatingState() };
+ auto isFirstLine = !formattingState().displayInlineContent() || formattingState().displayInlineContent()->lineBoxes.isEmpty();
+ auto lineBuilder = LineBuilder { *this, floatingContext, root(), inlineItems };
while (!needsLayoutRange.isEmpty()) {
- auto lineConstraints = constraintsForLine(constraints.horizontal, lineLogicalTop);
- line.open(lineConstraints.availableLogicalWidth);
- line.setHasIntrusiveFloat(lineConstraints.lineIsConstrainedByFloat);
// Turn previous line's overflow content length into the next line's leading content partial length.
// "sp[<-line break->]lit_content" -> overflow length: 11 -> leading partial content length: 11.
auto partialLeadingContentLength = previousLine ? previousLine->overflowContentLength : WTF::nullopt;
- auto lineContent = lineBuilder.layoutInlineContent(line, needsLayoutRange, partialLeadingContentLength);
+ auto initialLineConstraints = ConstraintsForInFlowContent { constraints.horizontal, { lineLogicalTop, makeOptional(toLayoutUnit(quirks().initialLineHeight())) } };
+ auto lineContent = lineBuilder.layoutInlineContent(needsLayoutRange, partialLeadingContentLength, initialLineConstraints, isFirstLine);
+ setDisplayBoxesForLine(lineContent, constraints.horizontal);
auto lineContentRange = lineContent.inlineItemRange;
- auto isLastLineWithInlineContent = [&] {
- if (lineContent.partialContent)
- return false;
- if (lineContentRange.end == needsLayoutRange.end)
- return true;
- // Omit floats to see if this is the last line with inline content.
- for (auto i = needsLayoutRange.end; i--;) {
- if (!inlineItems[i].isFloat())
- return i == lineContentRange.end - 1;
- }
- // There has to be at least one non-float item.
- ASSERT_NOT_REACHED();
- return false;
- }();
- line.close(isLastLineWithInlineContent);
-
- auto lineIsVisuallyEmpty = line.isVisuallyEmpty() ? LineBox::IsLineVisuallyEmpty::Yes : LineBox::IsLineVisuallyEmpty::No;
- auto lastLine = isLastLineWithInlineContent ? LineBox::IsLastLineWithInlineContent::Yes : LineBox::IsLastLineWithInlineContent::No;
- auto lineBox = LineBox { *this, lineConstraints.logicalTopLeft, line.lineLogicalWidth(), line.contentLogicalWidth(), lineContent.runs, lineIsVisuallyEmpty, lastLine};
- setDisplayBoxesForLine(lineContent, lineBox, constraints.horizontal);
-
if (!lineContentRange.isEmpty()) {
ASSERT(needsLayoutRange.start < lineContentRange.end);
- lineLogicalTop = lineBox.logicalBottom();
+ isFirstLine = false;
+ lineLogicalTop = lineContent.lineBox.logicalBottom();
// When the trailing content is partial, we need to reuse the last InlineTextItem.
auto lastInlineItemNeedsPartialLayout = lineContent.partialContent.hasValue();
if (lastInlineItemNeedsPartialLayout) {
@@ -196,10 +176,9 @@
}
// Floats prevented us placing any content on the line.
ASSERT(lineContent.runs.isEmpty());
- ASSERT(line.hasIntrusiveFloat());
+ ASSERT(lineContent.hasIntrusiveFloat);
// Move the next line below the intrusive float.
- auto floatingContext = FloatingContext { root(), *this, formattingState().floatingState() };
- auto floatConstraints = floatingContext.constraints(lineLogicalTop, toLayoutUnit(lineBox.logicalBottom()));
+ auto floatConstraints = floatingContext.constraints(lineLogicalTop, toLayoutUnit(lineContent.lineBox.logicalBottom()));
ASSERT(floatConstraints.left || floatConstraints.right);
static auto inifitePoint = PointInContextRoot::max();
// In case of left and right constraints, we need to pick the one that's closer to the current line.
@@ -270,18 +249,14 @@
{
auto& inlineItems = formattingState().inlineItems();
auto maximumLineWidth = InlineLayoutUnit { };
- auto line = Line { *this };
- auto lineBuilder = LineBuilder { *this, root(), inlineItems };
+ auto floatingContext = FloatingContext { root(), *this, formattingState().floatingState() };
+ auto lineBuilder = LineBuilder { *this, floatingContext, root(), inlineItems };
auto layoutRange = LineBuilder::InlineItemRange { 0 , inlineItems.size() };
while (!layoutRange.isEmpty()) {
- // Only the horiztonal available width is constrained when computing intrinsic width.
- line.open(availableWidth);
- auto lineContent = lineBuilder.layoutInlineContent(line, layoutRange, { });
- // FIXME: Find out if min/max needs to set the isLastLine bit.
- line.close(false);
- layoutRange.start = lineContent.inlineItemRange.end;
+ auto intrinsicContent = lineBuilder.computedIntrinsicWidth(layoutRange, availableWidth);
+ layoutRange.start = intrinsicContent.inlineItemRange.end;
// FIXME: Use line logical left and right to take floats into account.
- maximumLineWidth = std::max(maximumLineWidth, line.contentLogicalWidth());
+ maximumLineWidth = std::max(maximumLineWidth, intrinsicContent.logicalWidth);
}
return maximumLineWidth;
}
@@ -415,78 +390,10 @@
}
}
-InlineFormattingContext::LineConstraints InlineFormattingContext::constraintsForLine(const HorizontalConstraints& horizontalConstraints, InlineLayoutUnit lineLogicalTop)
+void InlineFormattingContext::setDisplayBoxesForLine(const LineBuilder::LineContent& lineContent, const HorizontalConstraints& horizontalConstraints)
{
- auto lineLogicalLeft = horizontalConstraints.logicalLeft;
- auto lineLogicalRight = lineLogicalLeft + horizontalConstraints.logicalWidth;
- auto lineIsConstrainedByFloat = false;
-
- auto floatingContext = FloatingContext { root(), *this, formattingState().floatingState() };
- // Check for intruding floats and adjust logical left/available width for this line accordingly.
- if (!floatingContext.isEmpty()) {
- // FIXME: Add support for variable line height, where the intrusive floats should be probed as the line height grows.
- auto floatConstraints = floatingContext.constraints(toLayoutUnit(lineLogicalTop), toLayoutUnit(lineLogicalTop + quirks().initialLineHeight()));
- // Check if these constraints actually put limitation on the line.
- if (floatConstraints.left && floatConstraints.left->x <= lineLogicalLeft)
- floatConstraints.left = { };
-
- if (floatConstraints.right && floatConstraints.right->x >= lineLogicalRight)
- floatConstraints.right = { };
-
- lineIsConstrainedByFloat = floatConstraints.left || floatConstraints.right;
-
- if (floatConstraints.left && floatConstraints.right) {
- ASSERT(floatConstraints.left->x <= floatConstraints.right->x);
- lineLogicalRight = floatConstraints.right->x;
- lineLogicalLeft = floatConstraints.left->x;
- } else if (floatConstraints.left) {
- ASSERT(floatConstraints.left->x >= lineLogicalLeft);
- lineLogicalLeft = floatConstraints.left->x;
- } else if (floatConstraints.right) {
- ASSERT(floatConstraints.right->x >= lineLogicalLeft);
- lineLogicalRight = floatConstraints.right->x;
- }
- }
-
- auto computedTextIndent = [&] {
- // text-indent property specifies the indentation applied to lines of inline content in a block.
- // The indent is treated as a margin applied to the start edge of the line box.
- // Unless otherwise specified, only lines that are the first formatted line of an element are affected.
- // For example, the first line of an anonymous block box is only affected if it is the first child of its parent element.
- // FIXME: Add support for each-line.
- // [Integration] root()->parent() would normally produce a valid layout box.
- auto& root = this->root();
- auto isFormattingContextRootCandidateToTextIndent = !root.isAnonymous();
- if (root.isAnonymous()) {
- // Unless otherwise specified by the each-line and/or hanging keywords, only lines that are the first formatted line
- // of an element are affected.
- // For example, the first line of an anonymous block box is only affected if it is the first child of its parent element.
- isFormattingContextRootCandidateToTextIndent = RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextIntegrationEnabled()
- ? layoutState().isIntegratedRootBoxFirstChild()
- : root.parent().firstInFlowChild() == &root;
- }
- if (!isFormattingContextRootCandidateToTextIndent)
- return InlineLayoutUnit { };
- auto invertLineRange = false;
-#if ENABLE(CSS3_TEXT)
- invertLineRange = root.style().textIndentType() == TextIndentType::Hanging;
-#endif
- auto isFirstLine = formattingState().ensureDisplayInlineContent().lineBoxes.isEmpty();
- // text-indent: hanging inverts which lines are affected.
- // inverted line range -> all the lines except the first one.
- // !inverted line range -> first line gets the indent.
- auto shouldIndent = invertLineRange != isFirstLine;
- if (!shouldIndent)
- return InlineLayoutUnit { };
- return geometry().computedTextIndent(root, horizontalConstraints).valueOr(InlineLayoutUnit { });
- };
- lineLogicalLeft += computedTextIndent();
- return LineConstraints { { lineLogicalLeft, lineLogicalTop }, lineLogicalRight - lineLogicalLeft, lineIsConstrainedByFloat };
-}
-
-void InlineFormattingContext::setDisplayBoxesForLine(const LineBuilder::LineContent& lineContent, const LineBox& lineBox, const HorizontalConstraints& horizontalConstraints)
-{
auto& formattingState = this->formattingState();
+ auto& lineBox = lineContent.lineBox;
if (!lineContent.floats.isEmpty()) {
auto floatingContext = FloatingContext { root(), *this, formattingState.floatingState() };
// Move floats to their final position.
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h (266679 => 266680)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h 2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h 2020-09-06 15:59:35 UTC (rev 266680)
@@ -67,7 +67,6 @@
public:
ContentHeightAndMargin inlineBlockHeightAndMargin(const Box&, const HorizontalConstraints&, const OverrideVerticalValues&) const;
ContentWidthAndMargin inlineBlockWidthAndMargin(const Box&, const HorizontalConstraints&, const OverrideHorizontalValues&);
- Optional<InlineLayoutUnit> computedTextIndent(const ContainerBox& formattingContextRoot, const HorizontalConstraints&) const;
private:
friend class InlineFormattingContext;
@@ -88,13 +87,7 @@
void computeWidthAndMargin(const Box&, const HorizontalConstraints&);
void collectInlineContentIfNeeded();
- struct LineConstraints {
- InlineLayoutPoint logicalTopLeft;
- InlineLayoutUnit availableLogicalWidth { 0 };
- bool lineIsConstrainedByFloat { false };
- };
- LineConstraints constraintsForLine(const HorizontalConstraints&, InlineLayoutUnit lineLogicalTop);
- void setDisplayBoxesForLine(const LineBuilder::LineContent&, const LineBox&, const HorizontalConstraints&);
+ void setDisplayBoxesForLine(const LineBuilder::LineContent&, const HorizontalConstraints&);
void invalidateFormattingState(const InvalidationState&);
const InlineFormattingState& formattingState() const { return downcast<InlineFormattingState>(FormattingContext::formattingState()); }
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp (266679 => 266680)
--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp 2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp 2020-09-06 15:59:35 UTC (rev 266680)
@@ -75,15 +75,7 @@
return complicatedCases(layoutBox, horizontalConstraints, overrideVerticalValues);
}
-Optional<InlineLayoutUnit> InlineFormattingContext::Geometry::computedTextIndent(const ContainerBox& formattingContextRoot, const HorizontalConstraints& horizontalConstraints) const
-{
- auto textIndent = formattingContextRoot.style().textIndent();
- if (textIndent == RenderStyle::initialTextIndent())
- return { };
- return InlineLayoutUnit { minimumValueForLength(textIndent, horizontalConstraints.logicalWidth) };
}
-
}
-}
#endif
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.cpp (266679 => 266680)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.cpp 2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.cpp 2020-09-06 15:59:35 UTC (rev 266680)
@@ -409,7 +409,22 @@
}
}
+const InlineFormattingContext& LineBox::formattingContext() const
+{
+ return m_inlineFormattingContext;
}
+
+const Box& LineBox::root() const
+{
+ return formattingContext().root();
}
+LayoutState& LineBox::layoutState() const
+{
+ return formattingContext().layoutState();
+}
+
+}
+}
+
#endif
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h (266679 => 266680)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h 2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h 2020-09-06 15:59:35 UTC (rev 266680)
@@ -123,9 +123,9 @@
InlineLayoutUnit contentLogicalWidth() const { return m_contentLogicalWidth; }
- const InlineFormattingContext& formattingContext() const { return m_inlineFormattingContext; }
- const Box& root() const { return formattingContext().root(); }
- LayoutState& layoutState() const { return formattingContext().layoutState(); }
+ const InlineFormattingContext& formattingContext() const;
+ const Box& root() const;
+ LayoutState& layoutState() const;
private:
Display::InlineRect m_rect;
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp (266679 => 266680)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp 2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp 2020-09-06 15:59:35 UTC (rev 266680)
@@ -29,6 +29,7 @@
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "DisplayBox.h"
+#include "FloatingContext.h"
#include "InlineFormattingContext.h"
#include "LayoutBox.h"
#include "TextUtil.h"
@@ -280,30 +281,65 @@
return boxGeometry.width();
}
-LineBuilder::LineBuilder(const InlineFormattingContext& inlineFormattingContext, const ContainerBox& formattingContextRoot, const InlineItems& inlineItems)
+LineBuilder::LineBuilder(const InlineFormattingContext& inlineFormattingContext, const FloatingContext& floatingContext, const ContainerBox& formattingContextRoot, const InlineItems& inlineItems)
: m_inlineFormattingContext(inlineFormattingContext)
+ , m_floatingContext(floatingContext)
, m_formattingContextRoot(formattingContextRoot)
+ , m_line(inlineFormattingContext)
, m_inlineItems(inlineItems)
{
}
-LineBuilder::LineContent LineBuilder::layoutInlineContent(Line& line, const InlineItemRange& layoutRange, Optional<unsigned> partialLeadingContentLength)
+LineBuilder::LineContent LineBuilder::layoutInlineContent(const InlineItemRange& needsLayoutRange, Optional<unsigned> partialLeadingContentLength, const FormattingContext::ConstraintsForInFlowContent& initialConstraints, bool isFirstLine)
{
- ASSERT(m_floats.isEmpty());
+ auto usedConstraints = constraintsForLine(initialConstraints, isFirstLine);
+ initialize(usedConstraints);
+
+ auto committedContent = placeInlineContent(needsLayoutRange, partialLeadingContentLength);
+ auto committedRange = close(needsLayoutRange, committedContent);
+
+ auto lineLogicalTopLeft = InlineLayoutPoint { usedConstraints.logicalLeft, initialConstraints.vertical.logicalTop };
+ auto isLastLine = isLastLineWithInlineContent(committedRange, needsLayoutRange.end, committedContent.partialTrailingContent.hasValue());
+ auto lineIsVisuallyEmpty = m_line.isVisuallyEmpty() ? LineBox::IsLineVisuallyEmpty::Yes : LineBox::IsLineVisuallyEmpty::No;
+ return LineContent { committedContent.partialTrailingContent, committedRange, m_floats, m_line.hasIntrusiveFloat()
+ , LineBox { formattingContext(), lineLogicalTopLeft, m_line.lineLogicalWidth(), m_line.contentLogicalWidth(), m_line.runs(), lineIsVisuallyEmpty
+ , isLastLine ? LineBox::IsLastLineWithInlineContent::Yes : LineBox::IsLastLineWithInlineContent::No }
+ , m_line.runs() };
+}
+
+LineBuilder::IntrinsicContent LineBuilder::computedIntrinsicWidth(const InlineItemRange& needsLayoutRange, InlineLayoutUnit availableWidth)
+{
+ initialize({ { }, availableWidth, false });
+ auto committedContent = placeInlineContent(needsLayoutRange, { });
+ auto committedRange = close(needsLayoutRange, committedContent);
+ return { committedRange, m_line.contentLogicalWidth() };
+}
+
+void LineBuilder::initialize(const UsedConstraints& lineConstraints)
+{
+ m_floats.clear();
m_partialLeadingTextItem = { };
m_lastWrapOpportunityItem = { };
+
+ m_line.open(lineConstraints.availableLogicalWidth);
+ m_line.setHasIntrusiveFloat(lineConstraints.isConstrainedByFloat);
+}
+
+LineBuilder::CommittedContent LineBuilder::placeInlineContent(const InlineItemRange& needsLayoutRange, Optional<unsigned> partialLeadingContentLength)
+{
+ auto lineCandidate = LineCandidate { };
auto lineBreaker = LineBreaker { };
- auto currentItemIndex = layoutRange.start;
- unsigned committedInlineItemCount = 0;
- auto lineCandidate = LineCandidate { };
- while (currentItemIndex < layoutRange.end) {
+
+ auto currentItemIndex = needsLayoutRange.start;
+ size_t committedInlineItemCount = 0;
+ while (currentItemIndex < needsLayoutRange.end) {
// 1. Collect the set of runs that we can commit to the line as one entity e.g. <span>text_and_span_start_span_end</span>.
// 2. Apply floats and shrink the available horizontal space e.g. <span>intru_<div style="float: left"></div>sive_float</span>.
// 3. Check if the content fits the line and commit the content accordingly (full, partial or not commit at all).
// 4. Return if we are at the end of the line either by not being able to fit more content or because of an explicit line break.
- nextContentForLine(lineCandidate, currentItemIndex, layoutRange, partialLeadingContentLength, line.availableWidth() + line.trimmableTrailingWidth(), line.contentLogicalWidth());
+ nextContentForLine(lineCandidate, currentItemIndex, needsLayoutRange, partialLeadingContentLength, m_line.availableWidth() + m_line.trimmableTrailingWidth(), m_line.contentLogicalWidth());
// Now check if we can put this content on the current line.
- auto result = handleFloatsAndInlineContent(lineBreaker, line, layoutRange, lineCandidate);
+ auto result = handleFloatsAndInlineContent(lineBreaker, needsLayoutRange, lineCandidate);
committedInlineItemCount = result.committedCount.isRevert ? result.committedCount.value : committedInlineItemCount + result.committedCount.value;
auto& inlineContent = lineCandidate.inlineContent;
auto inlineContentIsFullyCommitted = inlineContent.runs().size() == result.committedCount.value && !result.partialContent;
@@ -311,33 +347,106 @@
if (inlineContentIsFullyCommitted && inlineContent.trailingLineBreak()) {
// Fully commited (or empty) content followed by a line break means "end of line".
- line.append(*inlineContent.trailingLineBreak(), { });
+ m_line.append(*inlineContent.trailingLineBreak(), { });
++committedInlineItemCount;
isEndOfLine = true;
}
if (isEndOfLine) {
// We can't place any more items on the current line.
- return close(line, layoutRange, committedInlineItemCount, result.partialContent);
+ return { committedInlineItemCount, result.partialContent };
}
- currentItemIndex = layoutRange.start + committedInlineItemCount + m_floats.size();
+ currentItemIndex = needsLayoutRange.start + committedInlineItemCount + m_floats.size();
partialLeadingContentLength = { };
}
// Looks like we've run out of runs.
- return close(line, layoutRange, committedInlineItemCount, { });
+ return { committedInlineItemCount, { } };
}
-LineBuilder::LineContent LineBuilder::close(const Line& line, const InlineItemRange& layoutRange, unsigned committedInlineItemCount, Optional<LineContent::PartialContent> partialContent)
+LineBuilder::InlineItemRange LineBuilder::close(const InlineItemRange& needsLayoutRange, const CommittedContent& committedContent)
{
- ASSERT_UNUSED(line, committedInlineItemCount || !m_floats.isEmpty() || line.hasIntrusiveFloat());
- auto numberOfCommittedItems = committedInlineItemCount + m_floats.size();
- auto trailingInlineItemIndex = layoutRange.start + numberOfCommittedItems - 1;
- auto lineRange = InlineItemRange { layoutRange.start, trailingInlineItemIndex + 1 };
- ASSERT(lineRange.end <= layoutRange.end);
+ ASSERT(committedContent.inlineItemCount || !m_floats.isEmpty() || m_line.hasIntrusiveFloat());
+ auto numberOfCommittedItems = committedContent.inlineItemCount + m_floats.size();
+ auto trailingInlineItemIndex = needsLayoutRange.start + numberOfCommittedItems - 1;
+ auto lineRange = InlineItemRange { needsLayoutRange.start, trailingInlineItemIndex + 1 };
+ ASSERT(lineRange.end <= needsLayoutRange.end);
// Adjust hyphenated line count.
- m_successiveHyphenatedLineCount = partialContent && partialContent->trailingContentHasHyphen ? m_successiveHyphenatedLineCount + 1 : 0;
- return LineContent { partialContent, lineRange, WTFMove(m_floats), line.runs() };
+ m_successiveHyphenatedLineCount = committedContent.partialTrailingContent && committedContent.partialTrailingContent->trailingContentHasHyphen ? m_successiveHyphenatedLineCount + 1 : 0;
+ m_line.close(isLastLineWithInlineContent(lineRange, needsLayoutRange.end, committedContent.partialTrailingContent.hasValue()));
+ return lineRange;
}
+LineBuilder::UsedConstraints LineBuilder::constraintsForLine(const FormattingContext::ConstraintsForInFlowContent& initialConstraints, bool isFirstLine)
+{
+ auto lineLogicalLeft = initialConstraints.horizontal.logicalLeft;
+ auto lineLogicalTop = initialConstraints.vertical.logicalTop;
+ auto lineLogicalRight = lineLogicalLeft + initialConstraints.horizontal.logicalWidth;
+ auto lineIsConstrainedByFloat = false;
+
+ // Check for intruding floats and adjust logical left/available width for this line accordingly.
+ if (!m_floatingContext.isEmpty()) {
+ // FIXME: Add support for variable line height, where the intrusive floats should be probed as the line height grows.
+ auto floatConstraints = m_floatingContext.constraints(toLayoutUnit(lineLogicalTop), toLayoutUnit(lineLogicalTop + *initialConstraints.vertical.logicalHeight));
+ // Check if these constraints actually put limitation on the line.
+ if (floatConstraints.left && floatConstraints.left->x <= lineLogicalLeft)
+ floatConstraints.left = { };
+
+ if (floatConstraints.right && floatConstraints.right->x >= lineLogicalRight)
+ floatConstraints.right = { };
+
+ lineIsConstrainedByFloat = floatConstraints.left || floatConstraints.right;
+
+ if (floatConstraints.left && floatConstraints.right) {
+ ASSERT(floatConstraints.left->x <= floatConstraints.right->x);
+ lineLogicalRight = floatConstraints.right->x;
+ lineLogicalLeft = floatConstraints.left->x;
+ } else if (floatConstraints.left) {
+ ASSERT(floatConstraints.left->x >= lineLogicalLeft);
+ lineLogicalLeft = floatConstraints.left->x;
+ } else if (floatConstraints.right) {
+ ASSERT(floatConstraints.right->x >= lineLogicalLeft);
+ lineLogicalRight = floatConstraints.right->x;
+ }
+ }
+
+ auto computedTextIndent = [&]() -> InlineLayoutUnit {
+ // text-indent property specifies the indentation applied to lines of inline content in a block.
+ // The indent is treated as a margin applied to the start edge of the line box.
+ // Unless otherwise specified, only lines that are the first formatted line of an element are affected.
+ // For example, the first line of an anonymous block box is only affected if it is the first child of its parent element.
+ // FIXME: Add support for each-line.
+ // [Integration] root()->parent() would normally produce a valid layout box.
+ auto& root = this->root();
+ auto isFormattingContextRootCandidateToTextIndent = !root.isAnonymous();
+ if (root.isAnonymous()) {
+ // Unless otherwise specified by the each-line and/or hanging keywords, only lines that are the first formatted line
+ // of an element are affected.
+ // For example, the first line of an anonymous block box is only affected if it is the first child of its parent element.
+ isFormattingContextRootCandidateToTextIndent = RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextIntegrationEnabled()
+ ? layoutState().isIntegratedRootBoxFirstChild()
+ : root.parent().firstInFlowChild() == &root;
+ }
+ if (!isFormattingContextRootCandidateToTextIndent)
+ return { };
+ auto invertLineRange = false;
+#if ENABLE(CSS3_TEXT)
+ invertLineRange = root.style().textIndentType() == TextIndentType::Hanging;
+#endif
+ // text-indent: hanging inverts which lines are affected.
+ // inverted line range -> all the lines except the first one.
+ // !inverted line range -> first line gets the indent.
+ auto shouldIndent = invertLineRange != isFirstLine;
+ if (!shouldIndent)
+ return { };
+
+ auto textIndent = root.style().textIndent();
+ if (textIndent == RenderStyle::initialTextIndent())
+ return { };
+ return { minimumValueForLength(textIndent, initialConstraints.horizontal.logicalWidth) };
+ };
+ lineLogicalLeft += computedTextIndent();
+ return UsedConstraints { lineLogicalLeft, lineLogicalRight - lineLogicalLeft, lineIsConstrainedByFloat };
+}
+
void LineBuilder::nextContentForLine(LineCandidate& lineCandidate, unsigned currentInlineItemIndex, const InlineItemRange& layoutRange, Optional<unsigned> partialLeadingContentLength, InlineLayoutUnit availableLineWidth, InlineLayoutUnit currentLogicalRight)
{
ASSERT(currentInlineItemIndex < layoutRange.end);
@@ -384,7 +493,7 @@
}
}
-void LineBuilder::commitFloats(Line& line, const LineCandidate& lineCandidate, CommitIntrusiveFloatsOnly commitIntrusiveOnly)
+void LineBuilder::commitFloats(const LineCandidate& lineCandidate, CommitIntrusiveFloatsOnly commitIntrusiveOnly)
{
auto& floatContent = lineCandidate.floatContent;
auto leftIntrusiveFloatsWidth = InlineLayoutUnit { };
@@ -405,21 +514,21 @@
rightIntrusiveFloatsWidth += floatCandidate.logicalWidth;
}
}
- line.setHasIntrusiveFloat(hasIntrusiveFloat);
+ m_line.setHasIntrusiveFloat(hasIntrusiveFloat);
if (leftIntrusiveFloatsWidth || rightIntrusiveFloatsWidth) {
if (leftIntrusiveFloatsWidth)
- line.moveLogicalLeft(leftIntrusiveFloatsWidth);
+ m_line.moveLogicalLeft(leftIntrusiveFloatsWidth);
if (rightIntrusiveFloatsWidth)
- line.moveLogicalRight(rightIntrusiveFloatsWidth);
+ m_line.moveLogicalRight(rightIntrusiveFloatsWidth);
}
}
-LineBuilder::Result LineBuilder::handleFloatsAndInlineContent(LineBreaker& lineBreaker, Line& line, const InlineItemRange& layoutRange, const LineCandidate& lineCandidate)
+LineBuilder::Result LineBuilder::handleFloatsAndInlineContent(LineBreaker& lineBreaker, const InlineItemRange& layoutRange, const LineCandidate& lineCandidate)
{
auto& inlineContent = lineCandidate.inlineContent;
auto& candidateRuns = inlineContent.runs();
if (candidateRuns.isEmpty()) {
- commitFloats(line, lineCandidate);
+ commitFloats(lineCandidate);
return { LineBreaker::IsEndOfLine::No };
}
@@ -433,9 +542,9 @@
auto& floatContent = lineCandidate.floatContent;
// Check if this new content fits.
- auto availableWidth = line.availableWidth() - floatContent.intrusiveWidth();
- auto isLineConsideredEmpty = line.isVisuallyEmpty() && !line.hasIntrusiveFloat();
- auto lineStatus = LineBreaker::LineStatus { availableWidth, line.trimmableTrailingWidth(), line.isTrailingRunFullyTrimmable(), isLineConsideredEmpty };
+ auto availableWidth = m_line.availableWidth() - floatContent.intrusiveWidth();
+ auto isLineConsideredEmpty = m_line.isVisuallyEmpty() && !m_line.hasIntrusiveFloat();
+ auto lineStatus = LineBreaker::LineStatus { availableWidth, m_line.trimmableTrailingWidth(), m_line.isTrailingRunFullyTrimmable(), isLineConsideredEmpty };
auto result = lineBreaker.shouldWrapInlineContent(candidateRuns, inlineContent.logicalWidth(), lineStatus);
if (result.lastWrapOpportunityItem)
m_lastWrapOpportunityItem = result.lastWrapOpportunityItem;
@@ -442,8 +551,8 @@
if (result.action == LineBreaker::Result::Action::Keep) {
// This continuous content can be fully placed on the current line including non-intrusive floats.
for (auto& run : candidateRuns)
- line.append(run.inlineItem, run.logicalWidth);
- commitFloats(line, lineCandidate);
+ m_line.append(run.inlineItem, run.logicalWidth);
+ commitFloats(lineCandidate);
return { result.isEndOfLine, { candidateRuns.size(), false } };
}
if (result.action == LineBreaker::Result::Action::Push) {
@@ -455,14 +564,14 @@
ASSERT(result.isEndOfLine == LineBreaker::IsEndOfLine::Yes);
// Not only this content can't be placed on the current line, but we even need to revert the line back to an earlier position.
ASSERT(m_lastWrapOpportunityItem);
- return { LineBreaker::IsEndOfLine::Yes, { rebuildLine(line, layoutRange), true } };
+ return { LineBreaker::IsEndOfLine::Yes, { rebuildLine(layoutRange), true } };
}
if (result.action == LineBreaker::Result::Action::Split) {
ASSERT(result.isEndOfLine == LineBreaker::IsEndOfLine::Yes);
// Commit the combination of full and partial content on the current line.
- commitFloats(line, lineCandidate, CommitIntrusiveFloatsOnly::Yes);
+ commitFloats(lineCandidate, CommitIntrusiveFloatsOnly::Yes);
ASSERT(result.partialTrailingContent);
- commitPartialContent(line, candidateRuns, *result.partialTrailingContent);
+ commitPartialContent(candidateRuns, *result.partialTrailingContent);
// When splitting multiple runs <span style="word-break: break-all">text</span><span>content</span>, we might end up splitting them at run boundary.
// It simply means we don't really have a partial run. Partial content yes, but not partial run.
auto trailingRunIndex = result.partialTrailingContent->trailingRunIndex;
@@ -480,7 +589,7 @@
return { LineBreaker::IsEndOfLine::No };
}
-void LineBuilder::commitPartialContent(Line& line, const LineBreaker::RunList& runs, const LineBreaker::Result::PartialTrailingContent& partialTrailingContent)
+void LineBuilder::commitPartialContent(const LineBreaker::RunList& runs, const LineBreaker::Result::PartialTrailingContent& partialTrailingContent)
{
for (size_t index = 0; index < runs.size(); ++index) {
auto& run = runs[index];
@@ -490,26 +599,26 @@
if (auto partialRun = partialTrailingContent.partialRun) {
auto& trailingInlineTextItem = downcast<InlineTextItem>(runs[partialTrailingContent.trailingRunIndex].inlineItem);
auto partialTrailingTextItem = trailingInlineTextItem.left(partialRun->length);
- line.appendPartialTrailingTextItem(partialTrailingTextItem, partialRun->logicalWidth, partialRun->needsHyphen);
+ m_line.appendPartialTrailingTextItem(partialTrailingTextItem, partialRun->logicalWidth, partialRun->needsHyphen);
return;
}
// The partial run is the last content to commit.
- line.append(run.inlineItem, run.logicalWidth);
+ m_line.append(run.inlineItem, run.logicalWidth);
return;
}
- line.append(run.inlineItem, run.logicalWidth);
+ m_line.append(run.inlineItem, run.logicalWidth);
}
}
-size_t LineBuilder::rebuildLine(Line& line, const InlineItemRange& layoutRange)
+size_t LineBuilder::rebuildLine(const InlineItemRange& layoutRange)
{
// Clear the line and start appending the inline items closing with the last wrap opportunity run.
- line.clearContent();
+ m_line.clearContent();
auto currentItemIndex = layoutRange.start;
auto logicalRight = InlineLayoutUnit { };
if (m_partialLeadingTextItem) {
auto logicalWidth = inlineItemWidth(*m_partialLeadingTextItem, logicalRight);
- line.append(*m_partialLeadingTextItem, logicalWidth);
+ m_line.append(*m_partialLeadingTextItem, logicalWidth);
logicalRight += logicalWidth;
if (&m_partialLeadingTextItem.value() == m_lastWrapOpportunityItem)
return 1;
@@ -518,7 +627,7 @@
for (; currentItemIndex < layoutRange.end; ++currentItemIndex) {
auto& inlineItem = m_inlineItems[currentItemIndex];
auto logicalWidth = inlineItemWidth(inlineItem, logicalRight);
- line.append(inlineItem, logicalWidth);
+ m_line.append(inlineItem, logicalWidth);
logicalRight += logicalWidth;
if (&inlineItem == m_lastWrapOpportunityItem)
return currentItemIndex - layoutRange.start + 1;
@@ -526,7 +635,28 @@
return layoutRange.size();
}
+bool LineBuilder::isLastLineWithInlineContent(const InlineItemRange& lineRange, size_t lastInlineItemIndex, bool hasPartialTrailingContent) const
+{
+ if (hasPartialTrailingContent)
+ return false;
+ if (lineRange.end == lastInlineItemIndex)
+ return true;
+ // Omit floats to see if this is the last line with inline content.
+ for (auto i = lastInlineItemIndex; i--;) {
+ if (!m_inlineItems[i].isFloat())
+ return i == lineRange.end - 1;
+ }
+ // There has to be at least one non-float item.
+ ASSERT_NOT_REACHED();
+ return false;
}
+
+const LayoutState& LineBuilder::layoutState() const
+{
+ return formattingContext().layoutState();
}
+}
+}
+
#endif
Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h (266679 => 266680)
--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h 2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h 2020-09-06 15:59:35 UTC (rev 266680)
@@ -28,16 +28,18 @@
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "InlineLine.h"
+#include "InlineLineBox.h"
#include "InlineLineBreaker.h"
namespace WebCore {
namespace Layout {
+class FloatingContext;
struct LineCandidate;
class LineBuilder {
public:
- LineBuilder(const InlineFormattingContext&, const ContainerBox& formattingContextRoot, const InlineItems&);
+ LineBuilder(const InlineFormattingContext&, const FloatingContext&, const ContainerBox& formattingContextRoot, const InlineItems&);
struct InlineItemRange {
bool isEmpty() const { return start == end; }
@@ -57,13 +59,21 @@
const InlineItem* item { nullptr };
};
using FloatList = Vector<Float>;
- FloatList floats;
+ const FloatList& floats;
+ bool hasIntrusiveFloat { false };
+ const LineBox lineBox;
const Line::RunList& runs;
};
- LineContent layoutInlineContent(Line&, const InlineItemRange&, Optional<unsigned> partialLeadingContentLength);
+ LineContent layoutInlineContent(const InlineItemRange&, Optional<unsigned> partialLeadingContentLength, const FormattingContext::ConstraintsForInFlowContent& initialLineConstraints, bool isFirstLine);
+ struct IntrinsicContent {
+ InlineItemRange inlineItemRange;
+ InlineLayoutUnit logicalWidth { 0 };
+ };
+ IntrinsicContent computedIntrinsicWidth(const InlineItemRange&, InlineLayoutUnit availableWidth);
+
private:
- void nextContentForLine(LineCandidate&, unsigned inlineItemIndex, const InlineItemRange& layoutRange, Optional<unsigned> overflowLength, InlineLayoutUnit availableLineWidth, InlineLayoutUnit currentLogicalRight);
+ void nextContentForLine(LineCandidate&, unsigned inlineItemIndex, const InlineItemRange& needsLayoutRange, Optional<unsigned> overflowLength, InlineLayoutUnit availableLineWidth, InlineLayoutUnit currentLogicalRight);
struct Result {
LineBreaker::IsEndOfLine isEndOfLine { LineBreaker::IsEndOfLine::No };
struct CommittedContentCount {
@@ -74,19 +84,35 @@
Optional <LineContent::PartialContent> partialContent { };
};
enum class CommitIntrusiveFloatsOnly { No, Yes };
- void commitFloats(Line&, const LineCandidate&, CommitIntrusiveFloatsOnly = CommitIntrusiveFloatsOnly::No);
- Result handleFloatsAndInlineContent(LineBreaker&, Line&, const InlineItemRange& layoutRange, const LineCandidate&);
- size_t rebuildLine(Line&, const InlineItemRange& layoutRange);
- void commitPartialContent(Line&, const LineBreaker::RunList&, const LineBreaker::Result::PartialTrailingContent&);
- LineContent close(const Line&, const InlineItemRange& layoutRange, unsigned committedInlineItemCount, Optional<LineContent::PartialContent>);
+ struct UsedConstraints {
+ InlineLayoutUnit logicalLeft { 0 };
+ InlineLayoutUnit availableLogicalWidth { 0 };
+ bool isConstrainedByFloat { false };
+ };
+ UsedConstraints constraintsForLine(const FormattingContext::ConstraintsForInFlowContent& initialLineConstraints, bool isFirstLine);
+ void commitFloats(const LineCandidate&, CommitIntrusiveFloatsOnly = CommitIntrusiveFloatsOnly::No);
+ Result handleFloatsAndInlineContent(LineBreaker&, const InlineItemRange& needsLayoutRange, const LineCandidate&);
+ size_t rebuildLine(const InlineItemRange& needsLayoutRange);
+ void commitPartialContent(const LineBreaker::RunList&, const LineBreaker::Result::PartialTrailingContent&);
+ void initialize(const UsedConstraints&);
+ struct CommittedContent {
+ size_t inlineItemCount { 0 };
+ Optional <LineContent::PartialContent> partialTrailingContent;
+ };
+ CommittedContent placeInlineContent(const InlineItemRange&, Optional<unsigned> partialLeadingContentLength);
+ InlineItemRange close(const InlineItemRange& needsLayoutRange, const CommittedContent&);
InlineLayoutUnit inlineItemWidth(const InlineItem&, InlineLayoutUnit contentLogicalLeft) const;
+ bool isLastLineWithInlineContent(const InlineItemRange& lineRange, size_t lastInlineItemIndex, bool hasPartialTrailingContent) const;
const InlineFormattingContext& formattingContext() const { return m_inlineFormattingContext; }
const ContainerBox& root() const { return m_formattingContextRoot; }
+ const LayoutState& layoutState() const;
const InlineFormattingContext& m_inlineFormattingContext;
+ const FloatingContext& m_floatingContext;
const ContainerBox& m_formattingContextRoot;
+ Line m_line;
const InlineItems& m_inlineItems;
LineContent::FloatList m_floats;
Optional<InlineTextItem> m_partialLeadingTextItem;