Diff
Modified: trunk/Source/WebCore/ChangeLog (281478 => 281479)
--- trunk/Source/WebCore/ChangeLog 2021-08-23 23:52:10 UTC (rev 281478)
+++ trunk/Source/WebCore/ChangeLog 2021-08-24 00:20:27 UTC (rev 281479)
@@ -1,3 +1,32 @@
+2021-08-23 Alan Bujtas <za...@apple.com>
+
+ [LFC][IFC] Decouple line box building and vertical aligning
+ https://bugs.webkit.org/show_bug.cgi?id=229162
+ <rdar://problem/82260272>
+
+ Reviewed by Antti Koivisto.
+
+ This is in preparation for supporting incremental inline layout.
+ We should be able to vertically align the inline level boxes on any line without rebuilding the LineBox.
+
+ * Sources.txt:
+ * WebCore.xcodeproj/project.pbxproj:
+ * layout/formattingContexts/inline/InlineFormattingGeometry.cpp:
+ (WebCore::Layout::LineBoxBuilder::build):
+ (WebCore::Layout::LineBoxBuilder::constructAndAlignInlineLevelBoxes):
+ (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::rootInlineBoxLogicalTop const): Deleted.
+ (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::lineBoxHeight const): Deleted.
+ (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::setEnabled): Deleted.
+ (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::isEnabled const): Deleted.
+ (): Deleted.
+ (WebCore::Layout::LineBoxBuilder::computeLineBoxHeightAndAlignInlineLevelBoxesVertically): Deleted.
+ (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::SimplifiedVerticalAlignment): Deleted.
+ (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::canUseSimplifiedAlignment): Deleted.
+ (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::align): Deleted.
+ (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::adjust): Deleted.
+ * layout/formattingContexts/inline/InlineLevelBox.h:
+ * layout/formattingContexts/inline/InlineLineBox.h:
+
2021-08-23 Alex Christensen <achristen...@webkit.org>
Setting window.location.href to an invalid URL should throw a TypeError
Modified: trunk/Source/WebCore/Sources.txt (281478 => 281479)
--- trunk/Source/WebCore/Sources.txt 2021-08-23 23:52:10 UTC (rev 281478)
+++ trunk/Source/WebCore/Sources.txt 2021-08-24 00:20:27 UTC (rev 281479)
@@ -1460,6 +1460,7 @@
layout/formattingContexts/inline/InlineLine.cpp
layout/formattingContexts/inline/InlineLineBox.cpp
layout/formattingContexts/inline/InlineLineBuilder.cpp
+layout/formattingContexts/inline/InlineLineBoxVerticalAligner.cpp
layout/formattingContexts/inline/InlineTextItem.cpp
layout/formattingContexts/inline/text/TextUtil.cpp
layout/integration/LayoutIntegrationBoxTree.cpp
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (281478 => 281479)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2021-08-23 23:52:10 UTC (rev 281478)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2021-08-24 00:20:27 UTC (rev 281479)
@@ -2147,6 +2147,7 @@
6F56B44726479E6200AAE257 /* FormattingQuirks.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F56B44626479E6100AAE257 /* FormattingQuirks.h */; settings = {ATTRIBUTES = (Private, ); }; };
6F6383F62427AF4A00DABA53 /* LayoutInitialContainingBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F6383F42427AF4900DABA53 /* LayoutInitialContainingBlock.h */; settings = {ATTRIBUTES = (Private, ); }; };
6F6DF36F264722EA0093E834 /* BlockMarginCollapse.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F6DF36E264722EA0093E834 /* BlockMarginCollapse.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 6F6EE74326D44EE300374CDA /* InlineLineBoxVerticalAligner.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F6EE74126D44EE200374CDA /* InlineLineBoxVerticalAligner.h */; settings = {ATTRIBUTES = (Private, ); }; };
6F7CA3C6208C2957002F29AB /* LayoutState.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F7CA3C4208C2956002F29AB /* LayoutState.h */; settings = {ATTRIBUTES = (Private, ); }; };
6F7CA3CA208C2B2E002F29AB /* InlineFormattingContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F7CA3C8208C2B2E002F29AB /* InlineFormattingContext.h */; settings = {ATTRIBUTES = (Private, ); }; };
6F91421125152138004E4FEA /* InlineLineGeometry.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F91420F25152137004E4FEA /* InlineLineGeometry.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -9975,6 +9976,8 @@
6F6638D4249E268B001925FC /* TableWrapperBlockFormattingQuirks.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TableWrapperBlockFormattingQuirks.cpp; sourceTree = "<group>"; };
6F69A79924D6FAB800E6B85D /* InlineLineBox.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InlineLineBox.cpp; sourceTree = "<group>"; };
6F6DF36E264722EA0093E834 /* BlockMarginCollapse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlockMarginCollapse.h; sourceTree = "<group>"; };
+ 6F6EE74126D44EE200374CDA /* InlineLineBoxVerticalAligner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InlineLineBoxVerticalAligner.h; sourceTree = "<group>"; };
+ 6F6EE74426D44F0300374CDA /* InlineLineBoxVerticalAligner.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InlineLineBoxVerticalAligner.cpp; sourceTree = "<group>"; };
6F70DEDD251126F300F0FC78 /* FlexFormattingGeometry.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FlexFormattingGeometry.cpp; sourceTree = "<group>"; };
6F73918C2106CEDD006AF262 /* LayoutUnits.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LayoutUnits.h; sourceTree = "<group>"; };
6F7B8CEC23626E6600C9FF15 /* InlineItem.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InlineItem.cpp; sourceTree = "<group>"; };
@@ -18042,6 +18045,8 @@
6F0CD694229ED32700C5994E /* InlineLine.h */,
6F69A79924D6FAB800E6B85D /* InlineLineBox.cpp */,
6FB2400323DFF12700796458 /* InlineLineBox.h */,
+ 6F6EE74426D44F0300374CDA /* InlineLineBoxVerticalAligner.cpp */,
+ 6F6EE74126D44EE200374CDA /* InlineLineBoxVerticalAligner.h */,
6F25B200220A85AB0000011B /* InlineLineBuilder.cpp */,
6F26EB46234004A5006906E2 /* InlineLineBuilder.h */,
6F91420F25152137004E4FEA /* InlineLineGeometry.h */,
@@ -32433,6 +32438,7 @@
6FAAE71326A2814B00E07502 /* InlineLevelBox.h in Headers */,
6F0CD695229ED32700C5994E /* InlineLine.h in Headers */,
6FB2400523DFF12800796458 /* InlineLineBox.h in Headers */,
+ 6F6EE74326D44EE300374CDA /* InlineLineBoxVerticalAligner.h in Headers */,
6F26EB48234004A5006906E2 /* InlineLineBuilder.h in Headers */,
6F91421125152138004E4FEA /* InlineLineGeometry.h in Headers */,
6FC3F9472516756700A49BEA /* InlineLineRun.h in Headers */,
Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingGeometry.cpp (281478 => 281479)
--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingGeometry.cpp 2021-08-23 23:52:10 UTC (rev 281478)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingGeometry.cpp 2021-08-24 00:20:27 UTC (rev 281479)
@@ -32,6 +32,7 @@
#include "FormattingContext.h"
#include "InlineFormattingContext.h"
#include "InlineFormattingQuirks.h"
+#include "InlineLineBoxVerticalAligner.h"
#include "LayoutBox.h"
#include "LayoutContainerBox.h"
#include "LayoutReplacedBox.h"
@@ -47,32 +48,8 @@
InlineFormattingGeometry::LineBoxAndGeometry build(const LineBuilder::LineContent&);
private:
- struct SimplifiedVerticalAlignment {
- SimplifiedVerticalAlignment(const InlineLevelBox& rootInlineBox);
-
- static bool canUseSimplifiedAlignment(const InlineLevelBox& rootInlineBox, const InlineLevelBox&, std::optional<const BoxGeometry> inlineLevelBoxGeometry);
-
- void align(InlineLevelBox&);
-
- InlineLayoutUnit rootInlineBoxLogicalTop() const { return m_rootInlineBoxLogicalTop; }
- InlineLayoutUnit lineBoxHeight() const { return m_lineBoxLogicalBottom - m_lineBoxLogicalTop; }
-
- void setEnabled(bool enabled) { m_isEnabled = enabled; }
- bool isEnabled() const { return m_isEnabled; }
-
- private:
- void adjust(const InlineLevelBox&);
-
- const InlineLevelBox& m_rootInlineBox;
- bool m_isEnabled { true };
- InlineLayoutUnit m_lineBoxLogicalTop { 0 };
- InlineLayoutUnit m_lineBoxLogicalBottom { 0 };
- InlineLayoutUnit m_rootInlineBoxLogicalTop { 0 };
- };
-
void setVerticalGeometryForInlineBox(InlineLevelBox&) const;
- void constructAndAlignInlineLevelBoxes(LineBox&, const Line::RunList&);
- void computeLineBoxHeightAndAlignInlineLevelBoxesVertically(LineBox&);
+ InlineLayoutUnit constructAndAlignInlineLevelBoxes(LineBox&, const Line::RunList&);
const InlineFormattingContext& formattingContext() const { return m_inlineFormattingContext; }
const Box& rootBox() const { return formattingContext().root(); }
@@ -82,7 +59,6 @@
private:
const InlineFormattingContext& m_inlineFormattingContext;
- InlineLayoutUnit m_logicalHeight { 0 };
};
static InlineLayoutUnit hangingGlyphWidth(InlineLayoutUnit extraHorizontalSpace, const Line::RunList& runs, bool isLastLineWithInlineContent)
@@ -165,10 +141,10 @@
auto contentLogicalLeft = Layout::horizontalAlignmentOffset(runs, rootBox().style().textAlign(), lineContent.lineLogicalWidth, contentLogicalWidth, lineContent.isLastLineWithInlineContent).value_or(InlineLayoutUnit { });
auto lineBox = LineBox { rootBox(), contentLogicalLeft, contentLogicalWidth, lineContent.nonSpanningInlineLevelBoxCount };
- constructAndAlignInlineLevelBoxes(lineBox, runs);
+ auto lineBoxLogicalHeight = constructAndAlignInlineLevelBoxes(lineBox, runs);
auto lineGeometry = [&] {
- auto lineBoxLogicalRect = InlineRect { lineContent.logicalTopLeft, lineContent.lineLogicalWidth, m_logicalHeight };
+ auto lineBoxLogicalRect = InlineRect { lineContent.logicalTopLeft, lineContent.lineLogicalWidth, lineBoxLogicalHeight };
auto& rootInlineBox = lineBox.rootInlineBox();
auto enclosingTopAndBottom = LineGeometry::EnclosingTopAndBottom { rootInlineBox.logicalTop(), rootInlineBox.logicalBottom() };
@@ -229,23 +205,18 @@
inlineLevelBox.setLayoutBounds(InlineLevelBox::LayoutBounds { floorf(ascent), ceil(descent) });
}
-void LineBoxBuilder::constructAndAlignInlineLevelBoxes(LineBox& lineBox, const Line::RunList& runs)
+InlineLayoutUnit LineBoxBuilder::constructAndAlignInlineLevelBoxes(LineBox& lineBox, const Line::RunList& runs)
{
auto& rootInlineBox = lineBox.rootInlineBox();
setVerticalGeometryForInlineBox(rootInlineBox);
- auto simplifiedVerticalAlignment = SimplifiedVerticalAlignment { rootInlineBox };
// FIXME: Add fast path support for line-height content.
- simplifiedVerticalAlignment.setEnabled(layoutState().inStandardsMode() && rootBox().style().lineHeight().isNegative());
-
- auto simplifiedAlignVerticallyIfApplicable = [&](auto& inlineLevelBox, std::optional<const BoxGeometry> boxGeometry) {
- if (!simplifiedVerticalAlignment.isEnabled())
+ // FIXME: We should always be able to exercise the fast path when the line has no content at all, even in non-standards mode or with line-height set.
+ auto canUseSimplifiedAlignment = layoutState().inStandardsMode() && rootBox().style().lineHeight().isNegative();
+ auto updateCanUseSimplifiedAlignment = [&](auto& inlineLevelBox, std::optional<const BoxGeometry> boxGeometry = std::nullopt) {
+ if (!canUseSimplifiedAlignment)
return;
- if (!SimplifiedVerticalAlignment::canUseSimplifiedAlignment(rootInlineBox, inlineLevelBox, boxGeometry)) {
- simplifiedVerticalAlignment.setEnabled(false);
- return;
- }
- simplifiedVerticalAlignment.align(inlineLevelBox);
+ canUseSimplifiedAlignment = LineBoxVerticalAligner::canUseSimplifiedAlignmentForInlineLevelBox(rootInlineBox, inlineLevelBox, boxGeometry);
};
auto createWrappedInlineBoxes = [&] {
@@ -279,7 +250,7 @@
for (auto* layoutBox : WTF::makeReversedRange(layoutBoxesWithoutInlineBoxes)) {
auto inlineBox = InlineLevelBox::createInlineBox(*layoutBox, rootInlineBox.logicalLeft(), rootInlineBox.logicalWidth());
setVerticalGeometryForInlineBox(inlineBox);
- simplifiedAlignVerticallyIfApplicable(inlineBox, { });
+ updateCanUseSimplifiedAlignment(inlineBox);
lineBox.addInlineLevelBox(WTFMove(inlineBox));
}
};
@@ -333,7 +304,7 @@
auto atomicInlineLevelBox = InlineLevelBox::createAtomicInlineLevelBox(layoutBox, logicalLeft, { inlineLevelBoxGeometry.borderBoxWidth(), marginBoxHeight });
atomicInlineLevelBox.setBaseline(ascent);
atomicInlineLevelBox.setLayoutBounds(InlineLevelBox::LayoutBounds { ascent, marginBoxHeight - ascent });
- simplifiedAlignVerticallyIfApplicable(atomicInlineLevelBox, inlineLevelBoxGeometry);
+ updateCanUseSimplifiedAlignment(atomicInlineLevelBox, inlineLevelBoxGeometry);
lineBox.addInlineLevelBox(WTFMove(atomicInlineLevelBox));
continue;
}
@@ -346,7 +317,7 @@
ASSERT(initialLogicalWidth >= 0);
auto inlineBox = InlineLevelBox::createInlineBox(layoutBox, logicalLeft + marginStart, initialLogicalWidth);
setVerticalGeometryForInlineBox(inlineBox);
- simplifiedAlignVerticallyIfApplicable(inlineBox, { });
+ updateCanUseSimplifiedAlignment(inlineBox);
lineBox.addInlineLevelBox(WTFMove(inlineBox));
continue;
}
@@ -363,7 +334,7 @@
// When the content pulls the </span> to the logical left direction (e.g. negative letter space)
// make sure we don't end up with negative logical width on the inline box.
inlineBox.setLogicalWidth(std::max(0.f, inlineBoxLogicalRight - inlineBox.logicalLeft()));
- simplifiedAlignVerticallyIfApplicable(inlineBox, { });
+ updateCanUseSimplifiedAlignment(inlineBox);
continue;
}
if (run.isText() || run.isSoftLineBreak()) {
@@ -374,7 +345,7 @@
if (run.isHardLineBreak()) {
auto lineBreakBox = InlineLevelBox::createLineBreakBox(layoutBox, logicalLeft);
setVerticalGeometryForInlineBox(lineBreakBox);
- simplifiedAlignVerticallyIfApplicable(lineBreakBox, { });
+ updateCanUseSimplifiedAlignment(lineBreakBox);
lineBox.addInlineLevelBox(WTFMove(lineBreakBox));
continue;
}
@@ -386,326 +357,12 @@
}
lineBox.setHasContent(lineHasContent);
- if (!lineHasContent) {
- m_logicalHeight = { };
- rootInlineBox.setLogicalTop(-rootInlineBox.baseline());
- } else if (simplifiedVerticalAlignment.isEnabled()) {
- // We should always be able to exercise the fast path when the line has no content at all, even in non-standards mode or with line-height set.
- m_logicalHeight = simplifiedVerticalAlignment.lineBoxHeight();
- rootInlineBox.setLogicalTop(simplifiedVerticalAlignment.rootInlineBoxLogicalTop());
- } else
- computeLineBoxHeightAndAlignInlineLevelBoxesVertically(lineBox);
-}
-void LineBoxBuilder::computeLineBoxHeightAndAlignInlineLevelBoxesVertically(LineBox& lineBox)
-{
- ASSERT(lineBox.hasContent());
- // This function (partially) implements:
- // 2.2. Layout Within Line Boxes
- // https://www.w3.org/TR/css-inline-3/#line-layout
- // 1. Compute the line box height using the layout bounds geometry. This height computation strictly uses layout bounds and not normal inline level box geometries.
- // 2. Compute the baseline/logical top position of the root inline box. Aligned boxes push the root inline box around inside the line box.
- // 3. Finally align the inline level boxes using (mostly) normal inline level box geometries.
- auto& rootInlineBox = lineBox.rootInlineBox();
- auto& formattingGeometry = formattingContext().formattingGeometry();
-
- auto computeLineBoxLogicalHeight = [&] {
- // Line box height computation is based on the layout bounds of the inline boxes and not their logical (ascent/descent) dimensions.
- struct AbsoluteTopAndBottom {
- InlineLayoutUnit top { 0 };
- InlineLayoutUnit bottom { 0 };
- };
- HashMap<const InlineLevelBox*, AbsoluteTopAndBottom> inlineLevelBoxAbsoluteTopAndBottomMap;
-
- auto minimumLogicalTop = std::optional<InlineLayoutUnit> { };
- auto maximumLogicalBottom = std::optional<InlineLayoutUnit> { };
- if (formattingGeometry.inlineLevelBoxAffectsLineBox(rootInlineBox, lineBox)) {
- minimumLogicalTop = InlineLayoutUnit { };
- maximumLogicalBottom = rootInlineBox.layoutBounds().height();
- inlineLevelBoxAbsoluteTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { *minimumLogicalTop, *maximumLogicalBottom });
- } else
- inlineLevelBoxAbsoluteTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { });
-
- Vector<InlineLevelBox*> lineBoxRelativeInlineLevelBoxes;
- for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
- auto& layoutBox = inlineLevelBox.layoutBox();
- if (inlineLevelBox.hasLineBoxRelativeAlignment()) {
- lineBoxRelativeInlineLevelBoxes.append(&inlineLevelBox);
- continue;
- }
- auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
- // Logical top is relative to the parent inline box's layout bounds.
- // Note that this logical top is not the final logical top of the inline level box.
- // This is the logical top in the context of the layout bounds geometry which may be very different from the inline box's normal geometry.
- auto logicalTop = InlineLayoutUnit { };
- switch (inlineLevelBox.verticalAlign()) {
- case VerticalAlign::Baseline: {
- auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().ascent;
- logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
- break;
- }
- case VerticalAlign::Middle: {
- auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2;
- logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
- break;
- }
- case VerticalAlign::BaselineMiddle: {
- auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2;
- logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
- break;
- }
- case VerticalAlign::Length: {
- auto& style = inlineLevelBox.style();
- auto logicalTopOffsetFromParentBaseline = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight()) + inlineLevelBox.layoutBounds().ascent;
- logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
- break;
- }
- case VerticalAlign::TextTop: {
- // Note that text-top aligns with the inline box's font metrics top (ascent) and not the layout bounds top.
- logicalTop = parentInlineBox.layoutBounds().ascent - parentInlineBox.baseline();
- break;
- }
- case VerticalAlign::TextBottom: {
- // Note that text-bottom aligns with the inline box's font metrics bottom (descent) and not the layout bounds bottom.
- auto parentInlineBoxLayoutBounds = parentInlineBox.layoutBounds();
- auto parentInlineBoxLogicalBottom = parentInlineBoxLayoutBounds.height() - parentInlineBoxLayoutBounds.descent + parentInlineBox.descent().value_or(InlineLayoutUnit());
- logicalTop = parentInlineBoxLogicalBottom - inlineLevelBox.layoutBounds().height();
- break;
- }
- case VerticalAlign::Sub: {
- auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().ascent - (parentInlineBox.style().fontCascade().size() / 5 + 1);
- logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
- break;
- }
- case VerticalAlign::Super: {
- auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().ascent + parentInlineBox.style().fontCascade().size() / 3 + 1;
- logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
- break;
- }
- default:
- ASSERT_NOT_IMPLEMENTED_YET();
- break;
- }
- auto parentInlineBoxAbsoluteTopAndBottom = inlineLevelBoxAbsoluteTopAndBottomMap.get(&parentInlineBox);
- auto absoluteLogicalTop = parentInlineBoxAbsoluteTopAndBottom.top + logicalTop;
- auto absoluteLogicalBottom = absoluteLogicalTop + inlineLevelBox.layoutBounds().height();
- inlineLevelBoxAbsoluteTopAndBottomMap.add(&inlineLevelBox, AbsoluteTopAndBottom { absoluteLogicalTop, absoluteLogicalBottom });
- // Stretch the min/max absolute values if applicable.
- if (formattingGeometry.inlineLevelBoxAffectsLineBox(inlineLevelBox, lineBox)) {
- minimumLogicalTop = std::min(minimumLogicalTop.value_or(absoluteLogicalTop), absoluteLogicalTop);
- maximumLogicalBottom = std::max(maximumLogicalBottom.value_or(absoluteLogicalBottom), absoluteLogicalBottom);
- }
- }
- // The line box height computation is as follows:
- // 1. Stretch the line box with the non-line-box relative aligned inline box absolute top and bottom values.
- // 2. Check if the line box relative aligned inline boxes (top, bottom etc) have enough room and stretch the line box further if needed.
- auto lineBoxLogicalHeight = maximumLogicalBottom.value_or(InlineLayoutUnit()) - minimumLogicalTop.value_or(InlineLayoutUnit());
- for (auto* lineBoxRelativeInlineLevelBox : lineBoxRelativeInlineLevelBoxes) {
- if (!formattingGeometry.inlineLevelBoxAffectsLineBox(*lineBoxRelativeInlineLevelBox, lineBox))
- continue;
- lineBoxLogicalHeight = std::max(lineBoxLogicalHeight, lineBoxRelativeInlineLevelBox->layoutBounds().height());
- }
- m_logicalHeight = lineBoxLogicalHeight;
- };
- computeLineBoxLogicalHeight();
-
- auto computeRootInlineBoxVerticalPosition = [&] {
- HashMap<const InlineLevelBox*, InlineLayoutUnit> inlineLevelBoxAbsoluteBaselineOffsetMap;
- inlineLevelBoxAbsoluteBaselineOffsetMap.add(&rootInlineBox, InlineLayoutUnit { });
-
- auto maximumTopOffsetFromRootInlineBoxBaseline = std::optional<InlineLayoutUnit> { };
- if (formattingGeometry.inlineLevelBoxAffectsLineBox(rootInlineBox, lineBox))
- maximumTopOffsetFromRootInlineBoxBaseline = rootInlineBox.layoutBounds().ascent;
-
- for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
- auto absoluteBaselineOffset = InlineLayoutUnit { };
- if (!inlineLevelBox.hasLineBoxRelativeAlignment()) {
- auto& layoutBox = inlineLevelBox.layoutBox();
- auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
- auto baselineOffsetFromParentBaseline = InlineLayoutUnit { };
-
- switch (inlineLevelBox.verticalAlign()) {
- case VerticalAlign::Baseline:
- baselineOffsetFromParentBaseline = { };
- break;
- case VerticalAlign::Middle: {
- auto logicalTopOffsetFromParentBaseline = (inlineLevelBox.layoutBounds().height() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2);
- baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.layoutBounds().ascent;
- break;
- }
- case VerticalAlign::BaselineMiddle: {
- auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2;
- baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.layoutBounds().ascent;
- break;
- }
- case VerticalAlign::Length: {
- auto& style = inlineLevelBox.style();
- auto verticalAlignOffset = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight());
- auto logicalTopOffsetFromParentBaseline = verticalAlignOffset + inlineLevelBox.baseline();
- baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.baseline();
- break;
- }
- case VerticalAlign::TextTop:
- baselineOffsetFromParentBaseline = parentInlineBox.baseline() - inlineLevelBox.layoutBounds().ascent;
- break;
- case VerticalAlign::TextBottom:
- baselineOffsetFromParentBaseline = inlineLevelBox.layoutBounds().descent - *parentInlineBox.descent();
- break;
- case VerticalAlign::Sub:
- baselineOffsetFromParentBaseline = -(parentInlineBox.style().fontCascade().size() / 5 + 1);
- break;
- case VerticalAlign::Super:
- baselineOffsetFromParentBaseline = parentInlineBox.style().fontCascade().size() / 3 + 1;
- break;
- default:
- ASSERT_NOT_IMPLEMENTED_YET();
- break;
- }
- absoluteBaselineOffset = inlineLevelBoxAbsoluteBaselineOffsetMap.get(&parentInlineBox) + baselineOffsetFromParentBaseline;
- } else {
- switch (inlineLevelBox.verticalAlign()) {
- case VerticalAlign::Top: {
- absoluteBaselineOffset = rootInlineBox.layoutBounds().ascent - inlineLevelBox.layoutBounds().ascent;
- break;
- }
- case VerticalAlign::Bottom: {
- absoluteBaselineOffset = inlineLevelBox.layoutBounds().descent - rootInlineBox.layoutBounds().descent;
- break;
- }
- default:
- ASSERT_NOT_IMPLEMENTED_YET();
- break;
- }
- }
- inlineLevelBoxAbsoluteBaselineOffsetMap.add(&inlineLevelBox, absoluteBaselineOffset);
- auto affectsRootInlineBoxVerticalPosition = formattingGeometry.inlineLevelBoxAffectsLineBox(inlineLevelBox, lineBox);
- if (affectsRootInlineBoxVerticalPosition) {
- auto topOffsetFromRootInlineBoxBaseline = absoluteBaselineOffset + inlineLevelBox.layoutBounds().ascent;
- if (maximumTopOffsetFromRootInlineBoxBaseline)
- maximumTopOffsetFromRootInlineBoxBaseline = std::max(*maximumTopOffsetFromRootInlineBoxBaseline, topOffsetFromRootInlineBoxBaseline);
- else {
- // We are is quirk mode and the root inline box has no content. The root inline box's baseline is anchored at 0.
- // However negative ascent (e.g negative top margin) can "push" the root inline box upwards and have a negative value.
- maximumTopOffsetFromRootInlineBoxBaseline = inlineLevelBox.layoutBounds().ascent >= 0
- ? std::max(0.0f, topOffsetFromRootInlineBoxBaseline)
- : topOffsetFromRootInlineBoxBaseline;
- }
- }
- }
- auto rootInlineBoxLogicalTop = maximumTopOffsetFromRootInlineBoxBaseline.value_or(0.f) - rootInlineBox.baseline();
- rootInlineBox.setLogicalTop(rootInlineBoxLogicalTop);
- };
- computeRootInlineBoxVerticalPosition();
-
- auto alignInlineLevelBoxes = [&] {
- for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
- auto& layoutBox = inlineLevelBox.layoutBox();
- auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
- auto logicalTop = InlineLayoutUnit { };
- switch (inlineLevelBox.verticalAlign()) {
- case VerticalAlign::Baseline:
- logicalTop = parentInlineBox.baseline() - inlineLevelBox.baseline();
- break;
- case VerticalAlign::Middle: {
- auto logicalTopOffsetFromParentBaseline = (inlineLevelBox.logicalHeight() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2);
- logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
- break;
- }
- case VerticalAlign::BaselineMiddle: {
- auto logicalTopOffsetFromParentBaseline = inlineLevelBox.logicalHeight() / 2;
- logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
- break;
- }
- case VerticalAlign::Length: {
- auto& style = inlineLevelBox.style();
- auto verticalAlignOffset = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight());
- auto logicalTopOffsetFromParentBaseline = verticalAlignOffset + inlineLevelBox.baseline();
- logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
- break;
- }
- case VerticalAlign::Sub: {
- auto logicalTopOffsetFromParentBaseline = inlineLevelBox.baseline() - (parentInlineBox.style().fontCascade().size() / 5 + 1);
- logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
- break;
- }
- case VerticalAlign::Super: {
- auto logicalTopOffsetFromParentBaseline = inlineLevelBox.baseline() + parentInlineBox.style().fontCascade().size() / 3 + 1;
- logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
- break;
- }
- // Note that (text)top/bottom align their layout bounds.
- case VerticalAlign::TextTop:
- logicalTop = inlineLevelBox.layoutBounds().ascent - inlineLevelBox.baseline();
- break;
- case VerticalAlign::TextBottom:
- logicalTop = parentInlineBox.logicalHeight() - inlineLevelBox.layoutBounds().descent - inlineLevelBox.baseline();
- break;
- case VerticalAlign::Top:
- // Note that this logical top is not relative to the parent inline box.
- logicalTop = inlineLevelBox.layoutBounds().ascent - inlineLevelBox.baseline();
- break;
- case VerticalAlign::Bottom:
- // Note that this logical top is not relative to the parent inline box.
- logicalTop = m_logicalHeight - inlineLevelBox.layoutBounds().descent - inlineLevelBox.baseline();
- break;
- default:
- ASSERT_NOT_IMPLEMENTED_YET();
- break;
- }
- inlineLevelBox.setLogicalTop(logicalTop);
- }
- };
- alignInlineLevelBoxes();
+ auto verticalAligner = LineBoxVerticalAligner { formattingContext() };
+ canUseSimplifiedAlignment = canUseSimplifiedAlignment || !lineHasContent;
+ return verticalAligner.computeLogicalHeightAndAlign(lineBox, canUseSimplifiedAlignment);
}
-LineBoxBuilder::SimplifiedVerticalAlignment::SimplifiedVerticalAlignment(const InlineLevelBox& rootInlineBox)
- : m_rootInlineBox(rootInlineBox)
-{
- adjust(rootInlineBox);
-}
-
-bool LineBoxBuilder::SimplifiedVerticalAlignment::canUseSimplifiedAlignment(const InlineLevelBox& rootInlineBox, const InlineLevelBox& inlineLevelBox, std::optional<const BoxGeometry> inlineLevelBoxGeometry)
-{
- if (inlineLevelBox.isAtomicInlineLevelBox()) {
- ASSERT(inlineLevelBoxGeometry);
- // Baseline aligned, non-stretchy direct children are considered to be simple for now.
- auto& layoutBox = inlineLevelBox.layoutBox();
- return &layoutBox.parent() == &rootInlineBox.layoutBox()
- && layoutBox.style().verticalAlign() == VerticalAlign::Baseline
- && !inlineLevelBoxGeometry->marginBefore()
- && !inlineLevelBoxGeometry->marginAfter()
- && inlineLevelBoxGeometry->marginBoxHeight() <= rootInlineBox.baseline();
- }
- if (inlineLevelBox.isLineBreakBox()) {
- // Baseline aligned, non-stretchy line breaks e.g. <div><span><br></span></div> but not <div><span style="font-size: 100px;"><br></span></div>.
- return inlineLevelBox.layoutBox().style().verticalAlign() == VerticalAlign::Baseline && inlineLevelBox.baseline() <= rootInlineBox.baseline();
- }
- if (inlineLevelBox.isInlineBox()) {
- // Baseline aligned, non-stretchy inline boxes e.g. <div><span></span></div> but not <div><span style="font-size: 100px;"></span></div>.
- return inlineLevelBox.layoutBox().style().verticalAlign() == VerticalAlign::Baseline && inlineLevelBox.layoutBounds() == rootInlineBox.layoutBounds();
- }
- return false;
-}
-
-void LineBoxBuilder::SimplifiedVerticalAlignment::align(InlineLevelBox& inlineLevelBox)
-{
- if (inlineLevelBox.isAtomicInlineLevelBox() || inlineLevelBox.isLineBreakBox() || inlineLevelBox.isInlineBox()) {
- // Only baseline alignment for now.
- inlineLevelBox.setLogicalTop(m_rootInlineBox.baseline() - inlineLevelBox.baseline());
- adjust(inlineLevelBox);
- return;
- }
- ASSERT_NOT_IMPLEMENTED_YET();
-}
-
-void LineBoxBuilder::SimplifiedVerticalAlignment::adjust(const InlineLevelBox& inlineLevelBox)
-{
- auto layoutBoundsLogicalTop = m_rootInlineBox.layoutBounds().ascent - inlineLevelBox.layoutBounds().ascent;
- m_lineBoxLogicalTop = std::min(m_lineBoxLogicalTop, layoutBoundsLogicalTop);
- m_lineBoxLogicalBottom = std::max(m_lineBoxLogicalBottom, layoutBoundsLogicalTop + inlineLevelBox.layoutBounds().height());
- m_rootInlineBoxLogicalTop = std::max(m_rootInlineBoxLogicalTop, inlineLevelBox.layoutBounds().ascent - m_rootInlineBox.baseline());
-}
-
InlineFormattingGeometry::InlineFormattingGeometry(const InlineFormattingContext& inlineFormattingContext)
: FormattingGeometry(inlineFormattingContext)
{
Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLevelBox.h (281478 => 281479)
--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLevelBox.h 2021-08-23 23:52:10 UTC (rev 281478)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLevelBox.h 2021-08-24 00:20:27 UTC (rev 281479)
@@ -37,7 +37,7 @@
class LineBox;
class LineBoxBuilder;
-struct SimplifiedVerticalAlignment;
+class LineBoxVerticalAligner;
class InlineLevelBox {
public:
@@ -84,9 +84,9 @@
InlineLevelBox() = default;
private:
- friend struct SimplifiedVerticalAlignment;
+ friend class LineBox;
friend class LineBoxBuilder;
- friend class LineBox;
+ friend class LineBoxVerticalAligner;
const InlineRect& logicalRect() const { return m_logicalRect; }
InlineLayoutUnit logicalTop() const { return m_logicalRect.top(); }
Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBox.h (281478 => 281479)
--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBox.h 2021-08-23 23:52:10 UTC (rev 281478)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBox.h 2021-08-24 00:20:27 UTC (rev 281479)
@@ -40,7 +40,7 @@
class BoxGeometry;
class InlineFormattingContext;
class LineBoxBuilder;
-struct SimplifiedVerticalAlignment;
+class LineBoxVerticalAligner;
// ____________________________________________________________ Line Box
// | --------------------
@@ -82,6 +82,7 @@
private:
friend class LineBoxBuilder;
+ friend class LineBoxVerticalAligner;
void addInlineLevelBox(InlineLevelBox&&);
InlineLevelBoxList& nonRootInlineLevelBoxes() { return m_nonRootInlineLevelBoxList; }
Added: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.cpp (0 => 281479)
--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.cpp (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.cpp 2021-08-24 00:20:27 UTC (rev 281479)
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "InlineLineBoxVerticalAligner.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+namespace WebCore {
+namespace Layout {
+
+LineBoxVerticalAligner::LineBoxVerticalAligner(const InlineFormattingContext& inlineFormattingContext)
+ : m_inlineFormattingGeometry(inlineFormattingContext)
+{
+}
+
+bool LineBoxVerticalAligner::canUseSimplifiedAlignmentForInlineLevelBox(const InlineLevelBox& rootInlineBox, const InlineLevelBox& inlineLevelBox, std::optional<const BoxGeometry> inlineLevelBoxGeometry)
+{
+ if (inlineLevelBox.isAtomicInlineLevelBox()) {
+ ASSERT(inlineLevelBoxGeometry);
+ // Baseline aligned, non-stretchy direct children are considered to be simple for now.
+ auto& layoutBox = inlineLevelBox.layoutBox();
+ return &layoutBox.parent() == &rootInlineBox.layoutBox()
+ && layoutBox.style().verticalAlign() == VerticalAlign::Baseline
+ && !inlineLevelBoxGeometry->marginBefore()
+ && !inlineLevelBoxGeometry->marginAfter()
+ && inlineLevelBoxGeometry->marginBoxHeight() <= rootInlineBox.baseline();
+ }
+ if (inlineLevelBox.isLineBreakBox()) {
+ // Baseline aligned, non-stretchy line breaks e.g. <div><span><br></span></div> but not <div><span style="font-size: 100px;"><br></span></div>.
+ return inlineLevelBox.layoutBox().style().verticalAlign() == VerticalAlign::Baseline && inlineLevelBox.baseline() <= rootInlineBox.baseline();
+ }
+ if (inlineLevelBox.isInlineBox()) {
+ // Baseline aligned, non-stretchy inline boxes e.g. <div><span></span></div> but not <div><span style="font-size: 100px;"></span></div>.
+ return inlineLevelBox.layoutBox().style().verticalAlign() == VerticalAlign::Baseline && inlineLevelBox.layoutBounds() == rootInlineBox.layoutBounds();
+ }
+ return false;
+}
+
+InlineLayoutUnit LineBoxVerticalAligner::computeLogicalHeightAndAlign(LineBox& lineBox, bool useSimplifiedAlignment) const
+{
+ if (useSimplifiedAlignment)
+ return simplifiedVerticalAlignment(lineBox);
+ // This function (partially) implements:
+ // 2.2. Layout Within Line Boxes
+ // https://www.w3.org/TR/css-inline-3/#line-layout
+ // 1. Compute the line box height using the layout bounds geometry. This height computation strictly uses layout bounds and not normal inline level box geometries.
+ // 2. Compute the baseline/logical top position of the root inline box. Aligned boxes push the root inline box around inside the line box.
+ // 3. Finally align the inline level boxes using (mostly) normal inline level box geometries.
+ ASSERT(lineBox.hasContent());
+ auto lineBoxLogicalHeight = computeLineBoxLogicalHeight(lineBox);
+ computeRootInlineBoxVerticalPosition(lineBox);
+ alignInlineLevelBoxes(lineBox, lineBoxLogicalHeight);
+ return lineBoxLogicalHeight;
+}
+
+InlineLayoutUnit LineBoxVerticalAligner::simplifiedVerticalAlignment(LineBox& lineBox) const
+{
+ auto& rootInlineBox = lineBox.rootInlineBox();
+ auto rootInlineBoxBaseline = rootInlineBox.baseline();
+
+ if (!lineBox.hasContent()) {
+ rootInlineBox.setLogicalTop(-rootInlineBoxBaseline);
+ return { };
+ }
+
+ auto rootInlineBoxLayoutBounds = rootInlineBox.layoutBounds();
+
+ auto lineBoxLogicalTop = InlineLayoutUnit { 0 };
+ auto lineBoxLogicalBottom = rootInlineBoxLayoutBounds.height();
+ auto rootInlineBoxLogicalTop = rootInlineBoxLayoutBounds.ascent - rootInlineBoxBaseline;
+ for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
+ // Only baseline alignment for now.
+ inlineLevelBox.setLogicalTop(rootInlineBoxBaseline - inlineLevelBox.baseline());
+ auto layoutBounds = inlineLevelBox.layoutBounds();
+
+ auto layoutBoundsLogicalTop = rootInlineBoxLayoutBounds.ascent - layoutBounds.ascent;
+ lineBoxLogicalTop = std::min(lineBoxLogicalTop, layoutBoundsLogicalTop);
+ lineBoxLogicalBottom = std::max(lineBoxLogicalBottom, layoutBoundsLogicalTop + layoutBounds.height());
+ rootInlineBoxLogicalTop = std::max(rootInlineBoxLogicalTop, layoutBounds.ascent - rootInlineBoxBaseline);
+ }
+ rootInlineBox.setLogicalTop(rootInlineBoxLogicalTop);
+ return lineBoxLogicalBottom - lineBoxLogicalTop;
+}
+
+InlineLayoutUnit LineBoxVerticalAligner::computeLineBoxLogicalHeight(LineBox& lineBox) const
+{
+ // This function (partially) implements:
+ // 2.2. Layout Within Line Boxes
+ // https://www.w3.org/TR/css-inline-3/#line-layout
+ // 1. Compute the line box height using the layout bounds geometry. This height computation strictly uses layout bounds and not normal inline level box geometries.
+ // 2. Compute the baseline/logical top position of the root inline box. Aligned boxes push the root inline box around inside the line box.
+ // 3. Finally align the inline level boxes using (mostly) normal inline level box geometries.
+ auto& rootInlineBox = lineBox.rootInlineBox();
+ auto& formattingGeometry = this->formattingGeometry();
+
+ // Line box height computation is based on the layout bounds of the inline boxes and not their logical (ascent/descent) dimensions.
+ struct AbsoluteTopAndBottom {
+ InlineLayoutUnit top { 0 };
+ InlineLayoutUnit bottom { 0 };
+ };
+ HashMap<const InlineLevelBox*, AbsoluteTopAndBottom> inlineLevelBoxAbsoluteTopAndBottomMap;
+
+ auto minimumLogicalTop = std::optional<InlineLayoutUnit> { };
+ auto maximumLogicalBottom = std::optional<InlineLayoutUnit> { };
+ if (formattingGeometry.inlineLevelBoxAffectsLineBox(rootInlineBox, lineBox)) {
+ minimumLogicalTop = InlineLayoutUnit { };
+ maximumLogicalBottom = rootInlineBox.layoutBounds().height();
+ inlineLevelBoxAbsoluteTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { *minimumLogicalTop, *maximumLogicalBottom });
+ } else
+ inlineLevelBoxAbsoluteTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { });
+
+ Vector<InlineLevelBox*> lineBoxRelativeInlineLevelBoxes;
+ for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
+ auto& layoutBox = inlineLevelBox.layoutBox();
+ if (inlineLevelBox.hasLineBoxRelativeAlignment()) {
+ lineBoxRelativeInlineLevelBoxes.append(&inlineLevelBox);
+ continue;
+ }
+ auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
+ // Logical top is relative to the parent inline box's layout bounds.
+ // Note that this logical top is not the final logical top of the inline level box.
+ // This is the logical top in the context of the layout bounds geometry which may be very different from the inline box's normal geometry.
+ auto logicalTop = InlineLayoutUnit { };
+ switch (inlineLevelBox.verticalAlign()) {
+ case VerticalAlign::Baseline: {
+ auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().ascent;
+ logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+ break;
+ }
+ case VerticalAlign::Middle: {
+ auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2;
+ logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+ break;
+ }
+ case VerticalAlign::BaselineMiddle: {
+ auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2;
+ logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+ break;
+ }
+ case VerticalAlign::Length: {
+ auto& style = inlineLevelBox.style();
+ auto logicalTopOffsetFromParentBaseline = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight()) + inlineLevelBox.layoutBounds().ascent;
+ logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+ break;
+ }
+ case VerticalAlign::TextTop: {
+ // Note that text-top aligns with the inline box's font metrics top (ascent) and not the layout bounds top.
+ logicalTop = parentInlineBox.layoutBounds().ascent - parentInlineBox.baseline();
+ break;
+ }
+ case VerticalAlign::TextBottom: {
+ // Note that text-bottom aligns with the inline box's font metrics bottom (descent) and not the layout bounds bottom.
+ auto parentInlineBoxLayoutBounds = parentInlineBox.layoutBounds();
+ auto parentInlineBoxLogicalBottom = parentInlineBoxLayoutBounds.height() - parentInlineBoxLayoutBounds.descent + parentInlineBox.descent().value_or(InlineLayoutUnit());
+ logicalTop = parentInlineBoxLogicalBottom - inlineLevelBox.layoutBounds().height();
+ break;
+ }
+ case VerticalAlign::Sub: {
+ auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().ascent - (parentInlineBox.style().fontCascade().size() / 5 + 1);
+ logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+ break;
+ }
+ case VerticalAlign::Super: {
+ auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().ascent + parentInlineBox.style().fontCascade().size() / 3 + 1;
+ logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+ break;
+ }
+ default:
+ ASSERT_NOT_IMPLEMENTED_YET();
+ break;
+ }
+ auto parentInlineBoxAbsoluteTopAndBottom = inlineLevelBoxAbsoluteTopAndBottomMap.get(&parentInlineBox);
+ auto absoluteLogicalTop = parentInlineBoxAbsoluteTopAndBottom.top + logicalTop;
+ auto absoluteLogicalBottom = absoluteLogicalTop + inlineLevelBox.layoutBounds().height();
+ inlineLevelBoxAbsoluteTopAndBottomMap.add(&inlineLevelBox, AbsoluteTopAndBottom { absoluteLogicalTop, absoluteLogicalBottom });
+ // Stretch the min/max absolute values if applicable.
+ if (formattingGeometry.inlineLevelBoxAffectsLineBox(inlineLevelBox, lineBox)) {
+ minimumLogicalTop = std::min(minimumLogicalTop.value_or(absoluteLogicalTop), absoluteLogicalTop);
+ maximumLogicalBottom = std::max(maximumLogicalBottom.value_or(absoluteLogicalBottom), absoluteLogicalBottom);
+ }
+ }
+ // The line box height computation is as follows:
+ // 1. Stretch the line box with the non-line-box relative aligned inline box absolute top and bottom values.
+ // 2. Check if the line box relative aligned inline boxes (top, bottom etc) have enough room and stretch the line box further if needed.
+ auto lineBoxLogicalHeight = maximumLogicalBottom.value_or(InlineLayoutUnit()) - minimumLogicalTop.value_or(InlineLayoutUnit());
+ for (auto* lineBoxRelativeInlineLevelBox : lineBoxRelativeInlineLevelBoxes) {
+ if (!formattingGeometry.inlineLevelBoxAffectsLineBox(*lineBoxRelativeInlineLevelBox, lineBox))
+ continue;
+ lineBoxLogicalHeight = std::max(lineBoxLogicalHeight, lineBoxRelativeInlineLevelBox->layoutBounds().height());
+ }
+ return lineBoxLogicalHeight;
+}
+
+void LineBoxVerticalAligner::computeRootInlineBoxVerticalPosition(LineBox& lineBox) const
+{
+ auto& rootInlineBox = lineBox.rootInlineBox();
+ auto& formattingGeometry = this->formattingGeometry();
+
+ HashMap<const InlineLevelBox*, InlineLayoutUnit> inlineLevelBoxAbsoluteBaselineOffsetMap;
+ inlineLevelBoxAbsoluteBaselineOffsetMap.add(&rootInlineBox, InlineLayoutUnit { });
+
+ auto maximumTopOffsetFromRootInlineBoxBaseline = std::optional<InlineLayoutUnit> { };
+ if (formattingGeometry.inlineLevelBoxAffectsLineBox(rootInlineBox, lineBox))
+ maximumTopOffsetFromRootInlineBoxBaseline = rootInlineBox.layoutBounds().ascent;
+
+ for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
+ auto absoluteBaselineOffset = InlineLayoutUnit { };
+ if (!inlineLevelBox.hasLineBoxRelativeAlignment()) {
+ auto& layoutBox = inlineLevelBox.layoutBox();
+ auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
+ auto baselineOffsetFromParentBaseline = InlineLayoutUnit { };
+
+ switch (inlineLevelBox.verticalAlign()) {
+ case VerticalAlign::Baseline:
+ baselineOffsetFromParentBaseline = { };
+ break;
+ case VerticalAlign::Middle: {
+ auto logicalTopOffsetFromParentBaseline = (inlineLevelBox.layoutBounds().height() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2);
+ baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.layoutBounds().ascent;
+ break;
+ }
+ case VerticalAlign::BaselineMiddle: {
+ auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2;
+ baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.layoutBounds().ascent;
+ break;
+ }
+ case VerticalAlign::Length: {
+ auto& style = inlineLevelBox.style();
+ auto verticalAlignOffset = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight());
+ auto logicalTopOffsetFromParentBaseline = verticalAlignOffset + inlineLevelBox.baseline();
+ baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.baseline();
+ break;
+ }
+ case VerticalAlign::TextTop:
+ baselineOffsetFromParentBaseline = parentInlineBox.baseline() - inlineLevelBox.layoutBounds().ascent;
+ break;
+ case VerticalAlign::TextBottom:
+ baselineOffsetFromParentBaseline = inlineLevelBox.layoutBounds().descent - *parentInlineBox.descent();
+ break;
+ case VerticalAlign::Sub:
+ baselineOffsetFromParentBaseline = -(parentInlineBox.style().fontCascade().size() / 5 + 1);
+ break;
+ case VerticalAlign::Super:
+ baselineOffsetFromParentBaseline = parentInlineBox.style().fontCascade().size() / 3 + 1;
+ break;
+ default:
+ ASSERT_NOT_IMPLEMENTED_YET();
+ break;
+ }
+ absoluteBaselineOffset = inlineLevelBoxAbsoluteBaselineOffsetMap.get(&parentInlineBox) + baselineOffsetFromParentBaseline;
+ } else {
+ switch (inlineLevelBox.verticalAlign()) {
+ case VerticalAlign::Top: {
+ absoluteBaselineOffset = rootInlineBox.layoutBounds().ascent - inlineLevelBox.layoutBounds().ascent;
+ break;
+ }
+ case VerticalAlign::Bottom: {
+ absoluteBaselineOffset = inlineLevelBox.layoutBounds().descent - rootInlineBox.layoutBounds().descent;
+ break;
+ }
+ default:
+ ASSERT_NOT_IMPLEMENTED_YET();
+ break;
+ }
+ }
+ inlineLevelBoxAbsoluteBaselineOffsetMap.add(&inlineLevelBox, absoluteBaselineOffset);
+ auto affectsRootInlineBoxVerticalPosition = formattingGeometry.inlineLevelBoxAffectsLineBox(inlineLevelBox, lineBox);
+ if (affectsRootInlineBoxVerticalPosition) {
+ auto topOffsetFromRootInlineBoxBaseline = absoluteBaselineOffset + inlineLevelBox.layoutBounds().ascent;
+ if (maximumTopOffsetFromRootInlineBoxBaseline)
+ maximumTopOffsetFromRootInlineBoxBaseline = std::max(*maximumTopOffsetFromRootInlineBoxBaseline, topOffsetFromRootInlineBoxBaseline);
+ else {
+ // We are is quirk mode and the root inline box has no content. The root inline box's baseline is anchored at 0.
+ // However negative ascent (e.g negative top margin) can "push" the root inline box upwards and have a negative value.
+ maximumTopOffsetFromRootInlineBoxBaseline = inlineLevelBox.layoutBounds().ascent >= 0
+ ? std::max(0.0f, topOffsetFromRootInlineBoxBaseline)
+ : topOffsetFromRootInlineBoxBaseline;
+ }
+ }
+ }
+ auto rootInlineBoxLogicalTop = maximumTopOffsetFromRootInlineBoxBaseline.value_or(0.f) - rootInlineBox.baseline();
+ rootInlineBox.setLogicalTop(rootInlineBoxLogicalTop);
+}
+
+void LineBoxVerticalAligner::alignInlineLevelBoxes(LineBox& lineBox, InlineLayoutUnit lineBoxLogicalHeight) const
+{
+ for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
+ auto& layoutBox = inlineLevelBox.layoutBox();
+ auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
+ auto logicalTop = InlineLayoutUnit { };
+ switch (inlineLevelBox.verticalAlign()) {
+ case VerticalAlign::Baseline:
+ logicalTop = parentInlineBox.baseline() - inlineLevelBox.baseline();
+ break;
+ case VerticalAlign::Middle: {
+ auto logicalTopOffsetFromParentBaseline = (inlineLevelBox.logicalHeight() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2);
+ logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
+ break;
+ }
+ case VerticalAlign::BaselineMiddle: {
+ auto logicalTopOffsetFromParentBaseline = inlineLevelBox.logicalHeight() / 2;
+ logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
+ break;
+ }
+ case VerticalAlign::Length: {
+ auto& style = inlineLevelBox.style();
+ auto verticalAlignOffset = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight());
+ auto logicalTopOffsetFromParentBaseline = verticalAlignOffset + inlineLevelBox.baseline();
+ logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
+ break;
+ }
+ case VerticalAlign::Sub: {
+ auto logicalTopOffsetFromParentBaseline = inlineLevelBox.baseline() - (parentInlineBox.style().fontCascade().size() / 5 + 1);
+ logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
+ break;
+ }
+ case VerticalAlign::Super: {
+ auto logicalTopOffsetFromParentBaseline = inlineLevelBox.baseline() + parentInlineBox.style().fontCascade().size() / 3 + 1;
+ logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
+ break;
+ }
+ // Note that (text)top/bottom align their layout bounds.
+ case VerticalAlign::TextTop:
+ logicalTop = inlineLevelBox.layoutBounds().ascent - inlineLevelBox.baseline();
+ break;
+ case VerticalAlign::TextBottom:
+ logicalTop = parentInlineBox.logicalHeight() - inlineLevelBox.layoutBounds().descent - inlineLevelBox.baseline();
+ break;
+ case VerticalAlign::Top:
+ // Note that this logical top is not relative to the parent inline box.
+ logicalTop = inlineLevelBox.layoutBounds().ascent - inlineLevelBox.baseline();
+ break;
+ case VerticalAlign::Bottom:
+ // Note that this logical top is not relative to the parent inline box.
+ logicalTop = lineBoxLogicalHeight - inlineLevelBox.layoutBounds().descent - inlineLevelBox.baseline();
+ break;
+ default:
+ ASSERT_NOT_IMPLEMENTED_YET();
+ break;
+ }
+ inlineLevelBox.setLogicalTop(logicalTop);
+ }
+}
+
+}
+}
+
+#endif
Added: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.h (0 => 281479)
--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.h (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.h 2021-08-24 00:20:27 UTC (rev 281479)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "InlineFormattingGeometry.h"
+
+namespace WebCore {
+namespace Layout {
+
+class LineBoxVerticalAligner {
+public:
+ LineBoxVerticalAligner(const InlineFormattingContext&);
+ InlineLayoutUnit computeLogicalHeightAndAlign(LineBox&, bool useSimplifiedAlignment) const;
+
+ static bool canUseSimplifiedAlignmentForInlineLevelBox(const InlineLevelBox& rootInlineBox, const InlineLevelBox&, std::optional<const BoxGeometry> nlineLevelBoxGeometry);
+
+private:
+ InlineLayoutUnit simplifiedVerticalAlignment(LineBox&) const;
+
+ InlineLayoutUnit computeLineBoxLogicalHeight(LineBox&) const;
+ void computeRootInlineBoxVerticalPosition(LineBox&) const;
+ void alignInlineLevelBoxes(LineBox&, InlineLayoutUnit lineBoxLogicalHeight) const;
+
+ const InlineFormattingGeometry& formattingGeometry() const { return m_inlineFormattingGeometry; }
+
+private:
+ const InlineFormattingGeometry m_inlineFormattingGeometry;
+};
+
+}
+}
+
+#endif