WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-214527-20200718211211.patch (text/plain), 40.71 KB, created by
alan
on 2020-07-18 21:12:12 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
alan
Created:
2020-07-18 21:12:12 PDT
Size:
40.71 KB
patch
obsolete
>Subversion Revision: 262419 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 3df717ae1ce5b309e76ecc58804f4545d55dbe50..544f36dd6f854de9fcade3ab572e00b6824e6092 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,47 @@ >+2020-07-18 Zalan Bujtas <zalan@apple.com> >+ >+ [LFC][IFC] Move inline run alignment logic to a dedicated class >+ https://bugs.webkit.org/show_bug.cgi?id=214527 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ It will keep the code clean when we start introducing vertical aligment related state (which only required during >+ alignment, so adding the state to the LineBuilder class would be confusing). >+ >+ * layout/inlineformatting/InlineFormattingContext.h: >+ * layout/inlineformatting/InlineLineBuilder.cpp: >+ (WebCore::Layout::LineContentAligner::formattingContext const): >+ (WebCore::Layout::LineContentAligner::root const): >+ (WebCore::Layout::LineContentAligner::layoutState): >+ (WebCore::Layout::LineContentAligner::LineContentAligner): >+ (WebCore::Layout::LineContentAligner::alignHorizontally): >+ (WebCore::Layout::LineContentAligner::alignVertically): >+ (WebCore::Layout::LineContentAligner::justifyRuns): >+ (WebCore::Layout::LineContentAligner::alignRunsVertically): >+ (WebCore::Layout::LineContentAligner::adjustBaselineAndLineHeight): >+ (WebCore::Layout::LineContentAligner::runContentHeight const): >+ (WebCore::Layout::LineBuilder::LineBuilder): >+ (WebCore::Layout::LineBuilder::~LineBuilder): >+ (WebCore::Layout::LineBuilder::initialize): >+ (WebCore::Layout::LineBuilder::resetContent): >+ (WebCore::Layout::LineBuilder::close): >+ (WebCore::Layout::LineBuilder::appendInlineContainerEnd): >+ (WebCore::Layout::LineBuilder::alignContentVertically): Deleted. >+ (WebCore::Layout::LineBuilder::justifyRuns): Deleted. >+ (WebCore::Layout::LineBuilder::alignHorizontally): Deleted. >+ (WebCore::Layout::LineBuilder::adjustBaselineAndLineHeight): Deleted. >+ (WebCore::Layout::LineBuilder::runContentHeight const): Deleted. >+ (WebCore::Layout::LineBuilder::layoutState const): Deleted. >+ * layout/inlineformatting/InlineLineBuilder.h: >+ (WebCore::Layout::LineBuilder::logicalRight const): >+ (WebCore::Layout::LineBuilder::logicalWidth const): >+ (WebCore::Layout::LineBuilder::contentLogicalRight const): >+ (WebCore::Layout::LineBuilder::logicalTop const): Deleted. >+ (WebCore::Layout::LineBuilder::logicalBottom const): Deleted. >+ (WebCore::Layout::LineBuilder::logicalLeft const): Deleted. >+ (WebCore::Layout::LineBuilder::logicalHeight const): Deleted. >+ (WebCore::Layout::LineBuilder::baselineOffset const): Deleted. >+ > 2020-07-18 Zalan Bujtas <zalan@apple.com> > > [LFC][IFC] Add initial vertical-align: middle support >diff --git a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h >index bc19b2ffb8ba3b756c087dbe446c30f281b144cc..721b343fbc9228f55bf4eb32704d038d3add7a38 100644 >--- a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h >+++ b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h >@@ -94,7 +94,7 @@ private: > const InlineFormattingState& formattingState() const { return downcast<InlineFormattingState>(FormattingContext::formattingState()); } > InlineFormattingState& formattingState() { return downcast<InlineFormattingState>(FormattingContext::formattingState()); } > // FIXME: Come up with a structure that requires no friending. >- friend class LineBuilder; >+ friend class LineContentAligner; > }; > > inline InlineFormattingContext::Geometry::Geometry(const InlineFormattingContext& inlineFormattingContext) >diff --git a/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp b/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp >index f232f8c97ca0310c63a80f51118771d7c4e2be13..adfbc9cb4fe367d662a27517f27011e06363b927 100644 >--- a/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp >+++ b/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp >@@ -64,61 +64,93 @@ void HangingContent::reset() > m_width = 0; > } > >-LineBuilder::LineBuilder(const InlineFormattingContext& inlineFormattingContext, IntrinsicSizing intrinsicSizing) >- : m_inlineFormattingContext(inlineFormattingContext) >- , m_trimmableTrailingContent(m_runs) >- , m_isIntrinsicSizing(intrinsicSizing == IntrinsicSizing::Yes) >- , m_shouldIgnoreTrailingLetterSpacing(RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextIntegrationEnabled()) >-{ >-} >+class LineContentAligner { >+public: >+ LineContentAligner(const InlineFormattingContext&, InlineBox&, LineBuilder::RunList&); > >-LineBuilder::~LineBuilder() >+ void alignHorizontally(InlineLayoutUnit availableWidth, LineBuilder::IsLastLineWithInlineContent); >+ void alignVertically(Optional<InlineBox::Baseline> initialStrut); >+ >+private: >+ void adjustBaselineAndLineHeight(Optional<InlineBox::Baseline> initialStrut); >+ void alignRunsVertically(); >+ void justifyRuns(InlineLayoutUnit availableWidth); >+ InlineLayoutUnit runContentHeight(const LineBuilder::Run&) const; >+ >+ const InlineFormattingContext& formattingContext() const { return m_inlineFormattingContext; } >+ const ContainerBox& root() const { return formattingContext().root(); } >+ LayoutState& layoutState() { return formattingContext().layoutState(); } >+ >+ const InlineFormattingContext& m_inlineFormattingContext; >+ InlineBox& m_lineBox; >+ LineBuilder::RunList& m_runs; >+}; >+ >+LineContentAligner::LineContentAligner(const InlineFormattingContext& inlineFormattingContext, InlineBox& lineBox, LineBuilder::RunList& runs) >+ : m_inlineFormattingContext(inlineFormattingContext) >+ , m_lineBox(lineBox) >+ , m_runs(runs) > { > } > >-void LineBuilder::initialize(const Constraints& constraints) >+void LineContentAligner::alignHorizontally(InlineLayoutUnit availableWidth, LineBuilder::IsLastLineWithInlineContent isLastLine) > { >- ASSERT(m_isIntrinsicSizing || constraints.heightAndBaseline); >+ if (m_runs.isEmpty() || availableWidth <= 0) >+ return; > >- InlineLayoutUnit initialLineHeight = 0; >- InlineLayoutUnit initialBaselineOffset = 0; >- if (constraints.heightAndBaseline) { >- m_initialStrut = constraints.heightAndBaseline->strut; >- initialLineHeight = constraints.heightAndBaseline->height; >- initialBaselineOffset = constraints.heightAndBaseline->baselineOffset; >- } else >- m_initialStrut = { }; >+ auto computedHorizontalAlignment = [&] { >+ if (root().style().textAlign() != TextAlignMode::Justify) >+ return root().style().textAlign(); >+ // Text is justified according to the method specified by the text-justify property, >+ // in order to exactly fill the line box. Unless otherwise specified by text-align-last, >+ // the last line before a forced break or the end of the block is start-aligned. >+ if (m_runs.last().isLineBreak() || isLastLine == LineBuilder::IsLastLineWithInlineContent::Yes) >+ return TextAlignMode::Start; >+ return TextAlignMode::Justify; >+ }(); > >- auto lineRect = Display::InlineRect { constraints.logicalTopLeft, 0_lu, initialLineHeight }; >- auto baseline = InlineBox::Baseline { initialBaselineOffset, initialLineHeight - initialBaselineOffset }; >- m_lineBox = InlineBox { lineRect, baseline, initialBaselineOffset }; >- m_lineLogicalWidth = constraints.availableLogicalWidth; >- m_hasIntrusiveFloat = constraints.lineIsConstrainedByFloat; >+ if (computedHorizontalAlignment == TextAlignMode::Justify) { >+ justifyRuns(availableWidth); >+ return; >+ } > >- resetContent(); >-} >+ auto adjustmentForAlignment = [] (auto horizontalAlignment, auto availableWidth) -> Optional<InlineLayoutUnit> { >+ switch (horizontalAlignment) { >+ case TextAlignMode::Left: >+ case TextAlignMode::WebKitLeft: >+ case TextAlignMode::Start: >+ return { }; >+ case TextAlignMode::Right: >+ case TextAlignMode::WebKitRight: >+ case TextAlignMode::End: >+ return std::max<InlineLayoutUnit>(availableWidth, 0); >+ case TextAlignMode::Center: >+ case TextAlignMode::WebKitCenter: >+ return std::max<InlineLayoutUnit>(availableWidth / 2, 0); >+ case TextAlignMode::Justify: >+ ASSERT_NOT_REACHED(); >+ break; >+ } >+ ASSERT_NOT_REACHED(); >+ return { }; >+ }; > >-void LineBuilder::resetContent() >-{ >- m_lineBox.setLogicalWidth({ }); >- m_lineBox.setIsConsideredEmpty(); >- m_runs.clear(); >- m_trimmableTrailingContent.reset(); >- m_lineIsVisuallyEmptyBeforeTrimmableTrailingContent = { }; >+ auto adjustment = adjustmentForAlignment(computedHorizontalAlignment, availableWidth); >+ if (!adjustment) >+ return; >+ // Horizontal alignment means that we not only adjust the runs but also make sure >+ // that the line box is aligned as well >+ // e.g. <div style="text-align: center; width: 100px;">centered text</div> : the line box will also be centered >+ // as opposed to start at 0px all the way to [centered text] run's right edge. >+ m_lineBox.moveHorizontally(*adjustment); >+ for (auto& run : m_runs) >+ run.moveHorizontally(*adjustment); > } > >-LineBuilder::RunList LineBuilder::close(IsLastLineWithInlineContent isLastLineWithInlineContent) >+void LineContentAligner::alignVertically(Optional<InlineBox::Baseline> initialStrut) > { >- // 1. Remove trimmable trailing content. >- // 2. Align merged runs both vertically and horizontally. >- removeTrailingTrimmableContent(); >- visuallyCollapsePreWrapOverflowContent(); >- if (m_isIntrinsicSizing) >- return WTFMove(m_runs); >- >- auto hangingContent = collectHangingContent(isLastLineWithInlineContent); >- adjustBaselineAndLineHeight(); >- if (isVisuallyEmpty()) { >+ adjustBaselineAndLineHeight(initialStrut); >+ if (m_lineBox.isConsideredEmpty()) { > m_lineBox.resetBaseline(); > m_lineBox.setLogicalHeight({ }); > } >@@ -127,28 +159,61 @@ LineBuilder::RunList LineBuilder::close(IsLastLineWithInlineContent isLastLineWi > m_lineBox.shrinkVertically(m_lineBox.baseline().descent()); > m_lineBox.resetDescent(); > } >- alignContentVertically(); >- alignHorizontally(hangingContent, isLastLineWithInlineContent); >- return WTFMove(m_runs); >+ alignRunsVertically(); >+} >+ >+void LineContentAligner::justifyRuns(InlineLayoutUnit availableWidth) >+{ >+ ASSERT(availableWidth > 0); >+ // Collect the expansion opportunity numbers and find the last run with content. >+ auto expansionOpportunityCount = 0; >+ LineBuilder::Run* lastRunWithContent = nullptr; >+ for (auto& run : m_runs) { >+ expansionOpportunityCount += run.expansionOpportunityCount(); >+ if (run.isText() || run.isBox()) >+ lastRunWithContent = &run; >+ } >+ // Need to fix up the last run's trailing expansion. >+ if (lastRunWithContent && lastRunWithContent->hasExpansionOpportunity()) { >+ // Turn off the trailing bits first and add the forbid trailing expansion. >+ auto leadingExpansion = lastRunWithContent->expansionBehavior() & LeadingExpansionMask; >+ lastRunWithContent->setExpansionBehavior(leadingExpansion | ForbidTrailingExpansion); >+ } >+ // Nothing to distribute? >+ if (!expansionOpportunityCount) >+ return; >+ // Distribute the extra space. >+ auto expansionToDistribute = availableWidth / expansionOpportunityCount; >+ auto accumulatedExpansion = InlineLayoutUnit { }; >+ for (auto& run : m_runs) { >+ // Expand and moves runs by the accumulated expansion. >+ if (!run.hasExpansionOpportunity()) { >+ run.moveHorizontally(accumulatedExpansion); >+ continue; >+ } >+ ASSERT(run.expansionOpportunityCount()); >+ auto computedExpansion = expansionToDistribute * run.expansionOpportunityCount(); >+ run.setComputedHorizontalExpansion(computedExpansion); >+ run.moveHorizontally(accumulatedExpansion); >+ accumulatedExpansion += computedExpansion; >+ } > } > >-void LineBuilder::alignContentVertically() >+void LineContentAligner::alignRunsVertically() > { >- ASSERT(!m_isIntrinsicSizing); > auto scrollableOverflowRect = m_lineBox.logicalRect(); > for (auto& run : m_runs) { >- InlineLayoutUnit logicalTop = 0; >+ auto logicalTop = InlineLayoutUnit { }; > auto& layoutBox = run.layoutBox(); >- auto verticalAlign = layoutBox.style().verticalAlign(); >- auto ascent = layoutBox.style().fontMetrics().ascent(); > >- switch (verticalAlign) { >- case VerticalAlign::Baseline: >+ switch (layoutBox.style().verticalAlign()) { >+ case VerticalAlign::Baseline: { >+ auto ascent = layoutBox.style().fontMetrics().ascent(); > if (run.isLineBreak() || run.isText()) >- logicalTop = baselineOffset() - ascent; >+ logicalTop = m_lineBox.baselineOffset() - ascent; > else if (run.isContainerStart()) { > auto& boxGeometry = formattingContext().geometryForBox(layoutBox); >- logicalTop = baselineOffset() - ascent - boxGeometry.borderTop() - boxGeometry.paddingTop().valueOr(0); >+ logicalTop = m_lineBox.baselineOffset() - ascent - boxGeometry.borderTop() - boxGeometry.paddingTop().valueOr(0); > } else if (layoutBox.isInlineBlockBox() && layoutBox.establishesInlineFormattingContext()) { > auto& formattingState = layoutState().establishedInlineFormattingState(downcast<ContainerBox>(layoutBox)); > // Spec makes us generate at least one line -even if it is empty. >@@ -167,24 +232,19 @@ void LineBuilder::alignContentVertically() > // > auto& boxGeometry = formattingContext().geometryForBox(layoutBox); > auto baselineOffsetFromMarginBox = boxGeometry.marginBefore() + boxGeometry.borderTop() + boxGeometry.paddingTop().valueOr(0) + inlineBlockBaselineOffset; >- logicalTop = baselineOffset() - baselineOffsetFromMarginBox; >+ logicalTop = m_lineBox.baselineOffset() - baselineOffsetFromMarginBox; > } else { > auto& boxGeometry = formattingContext().geometryForBox(layoutBox); >- logicalTop = baselineOffset() - (boxGeometry.verticalBorder() + boxGeometry.verticalPadding().valueOr(0_lu) + run.logicalRect().height() + boxGeometry.marginAfter()); >+ logicalTop = m_lineBox.baselineOffset() - (boxGeometry.verticalBorder() + boxGeometry.verticalPadding().valueOr(0_lu) + run.logicalRect().height() + boxGeometry.marginAfter()); > } > break; >+ } > case VerticalAlign::Top: > logicalTop = 0_lu; > break; > case VerticalAlign::Bottom: >- logicalTop = logicalBottom() - run.logicalRect().height(); >- break; >- case VerticalAlign::Middle: { >- // Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent. >- auto xHeight = root().style().fontMetrics().xHeight(); >- logicalTop = m_lineBox.baselineOffset() - run.logicalRect().height() / 2 - xHeight / 2; >+ logicalTop = m_lineBox.logicalBottom() - run.logicalRect().height(); > break; >- } > default: > ASSERT_NOT_IMPLEMENTED_YET(); > break; >@@ -193,104 +253,200 @@ void LineBuilder::alignContentVertically() > // Adjust scrollable overflow if the run overflows the line. > scrollableOverflowRect.expandVerticallyToContain(run.logicalRect()); > // Convert runs from relative to the line top/left to the formatting root's border box top/left. >- run.moveVertically(this->logicalTop()); >- run.moveHorizontally(this->logicalLeft()); >+ run.moveVertically(m_lineBox.logicalTop()); >+ run.moveHorizontally(m_lineBox.logicalLeft()); > } > m_lineBox.setScrollableOverflow(scrollableOverflowRect); > } > >-void LineBuilder::justifyRuns(InlineLayoutUnit availableWidth) >+void LineContentAligner::adjustBaselineAndLineHeight(Optional<InlineBox::Baseline> initialStrut) > { >- ASSERT(availableWidth > 0); >- // Collect the expansion opportunity numbers and find the last run with content. >- auto expansionOpportunityCount = 0; >- Run* lastRunWithContent = nullptr; >- for (auto& run : m_runs) { >- expansionOpportunityCount += run.expansionOpportunityCount(); >- if (run.isText() || run.isBox()) >- lastRunWithContent = &run; >- } >- // Need to fix up the last run's trailing expansion. >- if (lastRunWithContent && lastRunWithContent->hasExpansionOpportunity()) { >- // Turn off the trailing bits first and add the forbid trailing expansion. >- auto leadingExpansion = lastRunWithContent->expansionBehavior() & LeadingExpansionMask; >- lastRunWithContent->setExpansionBehavior(leadingExpansion | ForbidTrailingExpansion); >- } >- // Nothing to distribute? >- if (!expansionOpportunityCount) >- return; >- // Distribute the extra space. >- auto expansionToDistribute = availableWidth / expansionOpportunityCount; >- InlineLayoutUnit accumulatedExpansion = 0; >+ unsigned inlineContainerNestingLevel = 0; >+ auto hasSeenDirectTextOrLineBreak = false; > for (auto& run : m_runs) { >- // Expand and moves runs by the accumulated expansion. >- if (!run.hasExpansionOpportunity()) { >- run.moveHorizontally(accumulatedExpansion); >+ auto& layoutBox = run.layoutBox(); >+ auto& style = layoutBox.style(); >+ >+ run.setLogicalHeight(runContentHeight(run)); >+ >+ if (run.isText() || run.isLineBreak()) { >+ // For text content we set the baseline either through the initial strut (set by the formatting context root) or >+ // through the inline container (start). Normally the text content itself does not stretch the line. >+ if (!initialStrut) { >+ // We are in standards mode where the baseline and line height are explicit. >+ continue; >+ } >+ if (inlineContainerNestingLevel) { >+ // We've already adjusted the line height/baseline through the parent inline container. >+ continue; >+ } >+ if (hasSeenDirectTextOrLineBreak) { >+ // e.g div>first text</div> or <div><span>nested<span>first direct text</div>. >+ continue; >+ } >+ hasSeenDirectTextOrLineBreak = true; >+ // We are in quirks mode where the font-metrics might change the line line height/baseline and this is the first text content on the line >+ // outside of an inline container. >+ m_lineBox.setAscentIfGreater(initialStrut->ascent()); >+ m_lineBox.setDescentIfGreater(initialStrut->descent()); >+ m_lineBox.setLogicalHeightIfGreater(initialStrut->height()); >+ continue; >+ } >+ >+ auto verticalAlign = style.verticalAlign(); >+ if (run.isContainerStart()) { >+ ++inlineContainerNestingLevel; >+ // Inline containers stretch the line by their font size. >+ // Vertical margins, paddings and borders don't contribute to the line height. >+ auto& fontMetrics = style.fontMetrics(); >+ switch (verticalAlign) { >+ case VerticalAlign::Baseline: { >+ auto halfLeading = LineBuilder::halfLeadingMetrics(fontMetrics, style.computedLineHeight()); >+ // Both halfleading ascent and descent could be negative (tall font vs. small line-height value) >+ if (halfLeading.descent() > 0) >+ m_lineBox.setDescentIfGreater(halfLeading.descent()); >+ if (halfLeading.ascent() > 0) >+ m_lineBox.setAscentIfGreater(halfLeading.ascent()); >+ m_lineBox.setLogicalHeightIfGreater(m_lineBox.baseline().height()); >+ break; >+ } >+ default: >+ m_lineBox.setLogicalHeightIfGreater(fontMetrics.height()); >+ break; >+ } >+ continue; >+ } >+ >+ if (run.isContainerEnd()) { >+ // The line's baseline and height have already been adjusted at ContainerStart. >+ ASSERT(inlineContainerNestingLevel); >+ --inlineContainerNestingLevel; >+ continue; >+ } >+ >+ if (run.isBox()) { >+ auto& boxGeometry = formattingContext().geometryForBox(layoutBox); >+ auto marginBoxHeight = boxGeometry.marginBoxHeight(); >+ >+ switch (verticalAlign) { >+ case VerticalAlign::Baseline: { >+ if (layoutBox.isInlineBlockBox() && layoutBox.establishesInlineFormattingContext()) { >+ // Inline-blocks with inline content always have baselines. >+ auto& formattingState = layoutState().establishedInlineFormattingState(downcast<ContainerBox>(layoutBox)); >+ // Spec makes us generate at least one line -even if it is empty. >+ auto& lastLineBox = formattingState.displayInlineContent()->lineBoxes.last(); >+ auto inlineBlockBaseline = lastLineBox.baseline(); >+ auto beforeHeight = boxGeometry.marginBefore() + boxGeometry.borderTop() + boxGeometry.paddingTop().valueOr(0); >+ >+ m_lineBox.setAscentIfGreater(inlineBlockBaseline.ascent()); >+ m_lineBox.setDescentIfGreater(inlineBlockBaseline.descent()); >+ m_lineBox.setBaselineOffsetIfGreater(beforeHeight + lastLineBox.baselineOffset()); >+ m_lineBox.setLogicalHeightIfGreater(marginBoxHeight); >+ } else { >+ // Non inline-block boxes sit on the baseline (including their bottom margin). >+ m_lineBox.setAscentIfGreater(marginBoxHeight); >+ // Ignore negative descent (yes, negative descent is a thing). >+ m_lineBox.setLogicalHeightIfGreater(marginBoxHeight + std::max<InlineLayoutUnit>(0, m_lineBox.baseline().descent())); >+ } >+ break; >+ } >+ case VerticalAlign::Top: >+ // Top align content never changes the baseline, it only pushes the bottom of the line further down. >+ m_lineBox.setLogicalHeightIfGreater(marginBoxHeight); >+ break; >+ case VerticalAlign::Bottom: { >+ // Bottom aligned, tall content pushes the baseline further down from the line top. >+ auto lineLogicalHeight = m_lineBox.logicalHeight(); >+ if (marginBoxHeight > lineLogicalHeight) { >+ m_lineBox.setLogicalHeightIfGreater(marginBoxHeight); >+ m_lineBox.setBaselineOffsetIfGreater(m_lineBox.baselineOffset() + (marginBoxHeight - lineLogicalHeight)); >+ } >+ break; >+ } >+ default: >+ ASSERT_NOT_IMPLEMENTED_YET(); >+ break; >+ } > continue; > } >- ASSERT(run.expansionOpportunityCount()); >- auto computedExpansion = expansionToDistribute * run.expansionOpportunityCount(); >- run.setComputedHorizontalExpansion(computedExpansion); >- run.moveHorizontally(accumulatedExpansion); >- accumulatedExpansion += computedExpansion; > } > } > >-void LineBuilder::alignHorizontally(const HangingContent& hangingContent, IsLastLineWithInlineContent isLastLine) >+InlineLayoutUnit LineContentAligner::runContentHeight(const LineBuilder::Run& run) const > { >- ASSERT(!m_isIntrinsicSizing); >+ auto& fontMetrics = run.style().fontMetrics(); >+ if (run.isText() || run.isLineBreak()) >+ return fontMetrics.height(); > >- auto availableWidth = this->availableWidth() + hangingContent.width(); >- if (m_runs.isEmpty() || availableWidth <= 0) >- return; >+ if (run.isContainerStart() || run.isContainerEnd()) >+ return fontMetrics.height(); > >- auto computedHorizontalAlignment = [&] { >- if (root().style().textAlign() != TextAlignMode::Justify) >- return root().style().textAlign(); >- // Text is justified according to the method specified by the text-justify property, >- // in order to exactly fill the line box. Unless otherwise specified by text-align-last, >- // the last line before a forced break or the end of the block is start-aligned. >- if (m_runs.last().isLineBreak() || isLastLine == IsLastLineWithInlineContent::Yes) >- return TextAlignMode::Start; >- return TextAlignMode::Justify; >- }(); >+ auto& layoutBox = run.layoutBox(); >+ auto& boxGeometry = formattingContext().geometryForBox(layoutBox); >+ if (layoutBox.isReplacedBox() || layoutBox.isFloatingPositioned()) >+ return boxGeometry.contentBoxHeight(); > >- if (computedHorizontalAlignment == TextAlignMode::Justify) { >- justifyRuns(availableWidth); >- return; >- } >+ // Non-replaced inline box (e.g. inline-block). It looks a bit misleading but their margin box is considered the content height here. >+ return boxGeometry.marginBoxHeight(); >+} > >- auto adjustmentForAlignment = [] (auto horizontalAlignment, auto availableWidth) -> Optional<InlineLayoutUnit> { >- switch (horizontalAlignment) { >- case TextAlignMode::Left: >- case TextAlignMode::WebKitLeft: >- case TextAlignMode::Start: >- return { }; >- case TextAlignMode::Right: >- case TextAlignMode::WebKitRight: >- case TextAlignMode::End: >- return std::max<InlineLayoutUnit>(availableWidth, 0); >- case TextAlignMode::Center: >- case TextAlignMode::WebKitCenter: >- return std::max<InlineLayoutUnit>(availableWidth / 2, 0); >- case TextAlignMode::Justify: >- ASSERT_NOT_REACHED(); >- break; >- } >- ASSERT_NOT_REACHED(); >- return { }; >- }; >+LineBuilder::LineBuilder(const InlineFormattingContext& inlineFormattingContext, IntrinsicSizing intrinsicSizing) >+ : m_inlineFormattingContext(inlineFormattingContext) >+ , m_trimmableTrailingContent(m_runs) >+ , m_isIntrinsicSizing(intrinsicSizing == IntrinsicSizing::Yes) >+ , m_shouldIgnoreTrailingLetterSpacing(RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextIntegrationEnabled()) >+{ >+} > >- auto adjustment = adjustmentForAlignment(computedHorizontalAlignment, availableWidth); >- if (!adjustment) >- return; >- // Horizontal alignment means that we not only adjust the runs but also make sure >- // that the line box is aligned as well >- // e.g. <div style="text-align: center; width: 100px;">centered text</div> : the line box will also be centered >- // as opposed to start at 0px all the way to [centered text] run's right edge. >- m_lineBox.moveHorizontally(*adjustment); >- for (auto& run : m_runs) >- run.moveHorizontally(*adjustment); >+LineBuilder::~LineBuilder() >+{ >+} >+ >+void LineBuilder::initialize(const Constraints& constraints) >+{ >+ ASSERT(m_isIntrinsicSizing || constraints.heightAndBaseline); >+ >+ InlineLayoutUnit initialLineHeight = 0; >+ InlineLayoutUnit initialBaselineOffset = 0; >+ if (constraints.heightAndBaseline) { >+ m_initialStrut = constraints.heightAndBaseline->strut; >+ initialLineHeight = constraints.heightAndBaseline->height; >+ initialBaselineOffset = constraints.heightAndBaseline->baselineOffset; >+ } else >+ m_initialStrut = { }; >+ >+ auto lineRect = Display::InlineRect { constraints.logicalTopLeft, 0_lu, initialLineHeight }; >+ auto baseline = InlineBox::Baseline { initialBaselineOffset, initialLineHeight - initialBaselineOffset }; >+ m_lineBox = InlineBox { lineRect, baseline, initialBaselineOffset }; >+ m_lineLogicalWidth = constraints.availableLogicalWidth; >+ m_hasIntrusiveFloat = constraints.lineIsConstrainedByFloat; >+ >+ resetContent(); >+} >+ >+void LineBuilder::resetContent() >+{ >+ m_lineBox.setLogicalWidth({ }); >+ m_lineBox.setIsConsideredEmpty(); >+ m_runs.clear(); >+ m_trimmableTrailingContent.reset(); >+ m_lineIsVisuallyEmptyBeforeTrimmableTrailingContent = { }; >+} >+ >+LineBuilder::RunList LineBuilder::close(IsLastLineWithInlineContent isLastLineWithInlineContent) >+{ >+ // 1. Remove trimmable trailing content. >+ // 2. Align merged runs both vertically and horizontally. >+ removeTrailingTrimmableContent(); >+ visuallyCollapsePreWrapOverflowContent(); >+ if (m_isIntrinsicSizing) >+ return WTFMove(m_runs); >+ >+ auto hangingContent = collectHangingContent(isLastLineWithInlineContent); >+ auto contentAligner = LineContentAligner { formattingContext(), m_lineBox, m_runs }; >+ contentAligner.alignHorizontally(availableWidth() + hangingContent.width(), isLastLineWithInlineContent); >+ contentAligner.alignVertically(m_initialStrut); >+ return WTFMove(m_runs); > } > > void LineBuilder::removeTrailingTrimmableContent() >@@ -466,7 +622,7 @@ void LineBuilder::appendInlineContainerEnd(const InlineItem& inlineItem, InlineL > // Prevent trailing letter-spacing from spilling out of the inline container. > // https://drafts.csswg.org/css-text-3/#letter-spacing-property See example 21. > removeTrailingLetterSpacing(); >- appendNonBreakableSpace(inlineItem, contentLogicalRight(), logicalWidth); >+ appendNonBreakableSpace(inlineItem, logicalRight(), logicalWidth); > } > > void LineBuilder::appendTextContent(const InlineTextItem& inlineTextItem, InlineLayoutUnit logicalWidth, bool needsHyphen) >@@ -553,167 +709,6 @@ void LineBuilder::appendLineBreak(const InlineItem& inlineItem) > m_runs.append({ downcast<InlineSoftLineBreakItem>(inlineItem), contentLogicalWidth() }); > } > >-void LineBuilder::adjustBaselineAndLineHeight() >-{ >- unsigned inlineContainerNestingLevel = 0; >- auto hasSeenDirectTextOrLineBreak = false; >- for (auto& run : m_runs) { >- auto& layoutBox = run.layoutBox(); >- auto& style = layoutBox.style(); >- >- run.setLogicalHeight(runContentHeight(run)); >- >- if (run.isText() || run.isLineBreak()) { >- // For text content we set the baseline either through the initial strut (set by the formatting context root) or >- // through the inline container (start). Normally the text content itself does not stretch the line. >- if (!m_initialStrut) { >- // We are in standards mode where the baseline and line height are explicit. >- continue; >- } >- if (inlineContainerNestingLevel) { >- // We've already adjusted the line height/baseline through the parent inline container. >- continue; >- } >- if (hasSeenDirectTextOrLineBreak) { >- // e.g div>first text</div> or <div><span>nested<span>first direct text</div>. >- continue; >- } >- hasSeenDirectTextOrLineBreak = true; >- // We are in quirks mode where the font-metrics might change the line line height/baseline and this is the first text content on the line >- // outside of an inline container. >- m_lineBox.setAscentIfGreater(m_initialStrut->ascent()); >- m_lineBox.setDescentIfGreater(m_initialStrut->descent()); >- m_lineBox.setLogicalHeightIfGreater(m_initialStrut->height()); >- continue; >- } >- >- auto middleVerticalAlign = [&](auto runLogicalHeight) { >- // Compute the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent >- // and check of we need to push the baseline or stretch the line. >- auto currentBaselineOffset = m_lineBox.baselineOffset(); >- auto parentXHeight = root().style().fontMetrics().xHeight(); >- auto logicalTop = currentBaselineOffset - runLogicalHeight / 2 - parentXHeight / 2; >- if (logicalTop < 0) { >- // Push the baseline down only if the run overflows at the top. >- auto offset = -logicalTop; >- m_lineBox.setBaselineOffsetIfGreater(currentBaselineOffset + offset); >- // Enough space for this inline container? >- auto spaceForRun = m_lineBox.logicalHeight() - runLogicalHeight; >- // Enough space for the descent? >- auto spaceForDescent = m_lineBox.logicalHeight() - (m_lineBox.baselineOffset() + m_lineBox.baseline().descent()); >- auto spaceForContent = std::min(spaceForRun, spaceForDescent); >- if (spaceForContent < 0) >- m_lineBox.setLogicalHeightIfGreater(m_lineBox.logicalHeight() - spaceForContent); >- return; >- } >- m_lineBox.setLogicalHeightIfGreater(logicalTop + runLogicalHeight); >- }; >- auto verticalAlign = style.verticalAlign(); >- if (run.isContainerStart()) { >- ++inlineContainerNestingLevel; >- // Inline containers stretch the line by their font size. >- // Vertical margins, paddings and borders don't contribute to the line height. >- auto& fontMetrics = style.fontMetrics(); >- switch (verticalAlign) { >- case VerticalAlign::Baseline: { >- auto halfLeading = halfLeadingMetrics(fontMetrics, style.computedLineHeight()); >- // Both halfleading ascent and descent could be negative (tall font vs. small line-height value) >- if (halfLeading.descent() > 0) >- m_lineBox.setDescentIfGreater(halfLeading.descent()); >- if (halfLeading.ascent() > 0) >- m_lineBox.setAscentIfGreater(halfLeading.ascent()); >- m_lineBox.setLogicalHeightIfGreater(m_lineBox.baseline().height()); >- break; >- } >- case VerticalAlign::Middle: { >- middleVerticalAlign(fontMetrics.height()); >- break; >- } >- default: >- m_lineBox.setLogicalHeightIfGreater(fontMetrics.height()); >- break; >- } >- continue; >- } >- >- if (run.isContainerEnd()) { >- // The line's baseline and height have already been adjusted at ContainerStart. >- ASSERT(inlineContainerNestingLevel); >- --inlineContainerNestingLevel; >- continue; >- } >- >- if (run.isBox()) { >- auto& boxGeometry = formattingContext().geometryForBox(layoutBox); >- auto marginBoxHeight = boxGeometry.marginBoxHeight(); >- >- switch (verticalAlign) { >- case VerticalAlign::Baseline: { >- if (layoutBox.isInlineBlockBox() && layoutBox.establishesInlineFormattingContext()) { >- // Inline-blocks with inline content always have baselines. >- auto& formattingState = layoutState().establishedInlineFormattingState(downcast<ContainerBox>(layoutBox)); >- // Spec makes us generate at least one line -even if it is empty. >- auto& lastLineBox = formattingState.displayInlineContent()->lineBoxes.last(); >- auto inlineBlockBaseline = lastLineBox.baseline(); >- auto beforeHeight = boxGeometry.marginBefore() + boxGeometry.borderTop() + boxGeometry.paddingTop().valueOr(0); >- >- m_lineBox.setAscentIfGreater(inlineBlockBaseline.ascent()); >- m_lineBox.setDescentIfGreater(inlineBlockBaseline.descent()); >- m_lineBox.setBaselineOffsetIfGreater(beforeHeight + lastLineBox.baselineOffset()); >- m_lineBox.setLogicalHeightIfGreater(marginBoxHeight); >- } else { >- // Non inline-block boxes sit on the baseline (including their bottom margin). >- m_lineBox.setAscentIfGreater(marginBoxHeight); >- // Ignore negative descent (yes, negative descent is a thing). >- m_lineBox.setLogicalHeightIfGreater(marginBoxHeight + std::max<InlineLayoutUnit>(0, m_lineBox.baseline().descent())); >- } >- break; >- } >- case VerticalAlign::Top: >- // Top align content never changes the baseline, it only pushes the bottom of the line further down. >- m_lineBox.setLogicalHeightIfGreater(marginBoxHeight); >- break; >- case VerticalAlign::Bottom: { >- // Bottom aligned, tall content pushes the baseline further down from the line top. >- auto lineLogicalHeight = m_lineBox.logicalHeight(); >- if (marginBoxHeight > lineLogicalHeight) { >- m_lineBox.setLogicalHeightIfGreater(marginBoxHeight); >- m_lineBox.setBaselineOffsetIfGreater(m_lineBox.baselineOffset() + (marginBoxHeight - lineLogicalHeight)); >- } >- break; >- } >- case VerticalAlign::Middle: { >- middleVerticalAlign(marginBoxHeight); >- break; >- } >- default: >- ASSERT_NOT_IMPLEMENTED_YET(); >- break; >- } >- continue; >- } >- } >-} >- >-InlineLayoutUnit LineBuilder::runContentHeight(const Run& run) const >-{ >- ASSERT(!m_isIntrinsicSizing); >- auto& fontMetrics = run.style().fontMetrics(); >- if (run.isText() || run.isLineBreak()) >- return fontMetrics.height(); >- >- if (run.isContainerStart() || run.isContainerEnd()) >- return fontMetrics.height(); >- >- auto& layoutBox = run.layoutBox(); >- auto& boxGeometry = formattingContext().geometryForBox(layoutBox); >- if (layoutBox.isReplacedBox() || layoutBox.isFloatingPositioned()) >- return boxGeometry.contentBoxHeight(); >- >- // Non-replaced inline box (e.g. inline-block). It looks a bit misleading but their margin box is considered the content height here. >- return boxGeometry.marginBoxHeight(); >-} >- > bool LineBuilder::isVisuallyNonEmpty(const Run& run) const > { > if (run.isText()) >@@ -760,11 +755,6 @@ InlineBox::Baseline LineBuilder::halfLeadingMetrics(const FontMetrics& fontMetri > return { adjustedAscent, adjustedDescent }; > } > >-LayoutState& LineBuilder::layoutState() const >-{ >- return formattingContext().layoutState(); >-} >- > const InlineFormattingContext& LineBuilder::formattingContext() const > { > return m_inlineFormattingContext; >diff --git a/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h b/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h >index 49feb90f6ca9a934f6636c7a3b2488d4323f97a2..c4b08b8cbf87af32af9689f7633457fcae4ce9cd 100644 >--- a/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h >+++ b/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h >@@ -38,10 +38,9 @@ namespace Layout { > struct HangingContent; > class InlineFormattingContext; > class InlineSoftLineBreakItem; >+class LineContentAligner; > > class LineBuilder { >- struct ContinuousContent; >- > public: > struct Constraints { > InlineLayoutPoint logicalTopLeft; >@@ -92,6 +91,7 @@ public: > Run& operator=(Run&& other) = default; > > private: >+ friend class LineContentAligner; > friend class LineBuilder; > > Run(const InlineTextItem&, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth, bool needsHypen); >@@ -151,18 +151,11 @@ public: > static InlineBox::Baseline halfLeadingMetrics(const FontMetrics&, InlineLayoutUnit lineLogicalHeight); > > private: >- InlineLayoutUnit logicalTop() const { return m_lineBox.logicalTop(); } >- InlineLayoutUnit logicalBottom() const { return m_lineBox.logicalBottom(); } >- >- InlineLayoutUnit logicalLeft() const { return m_lineBox.logicalLeft(); } >- InlineLayoutUnit logicalRight() const { return logicalLeft() + logicalWidth(); } >- >+ InlineLayoutUnit logicalRight() const { return m_lineBox.logicalLeft() + logicalWidth(); } > InlineLayoutUnit logicalWidth() const { return m_lineLogicalWidth; } >- InlineLayoutUnit logicalHeight() const { return m_lineBox.logicalHeight(); } > > InlineLayoutUnit contentLogicalWidth() const { return m_lineBox.logicalWidth(); } > InlineLayoutUnit contentLogicalRight() const { return m_lineBox.logicalRight(); } >- InlineLayoutUnit baselineOffset() const { return m_lineBox.baselineOffset(); } > > struct InlineRunDetails { > InlineLayoutUnit logicalWidth { 0 }; >@@ -180,17 +173,9 @@ private: > void removeTrailingTrimmableContent(); > void visuallyCollapsePreWrapOverflowContent(); > HangingContent collectHangingContent(IsLastLineWithInlineContent); >- void alignHorizontally(const HangingContent&, IsLastLineWithInlineContent); >- void alignContentVertically(); >- >- void adjustBaselineAndLineHeight(); >- InlineLayoutUnit runContentHeight(const Run&) const; >- >- void justifyRuns(InlineLayoutUnit availableWidth); > > bool isVisuallyNonEmpty(const Run&) const; > >- LayoutState& layoutState() const; > const InlineFormattingContext& formattingContext() const; > const ContainerBox& root() const; >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 214527
:
404652
|
404653
|
405680
|
407269
|
407293
|
407297