Skip to content

Commit

Permalink
[IFC] Incorrect decorating box position in vertical writing mode
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=255256

Reviewed by Antti Koivisto.

Incorrect bidi logic found its way in to non-bidi layout at 247113@main. Classic case of flipping coords twice incorrectly gets you the correct result in most cases.
This patch fixes them both.
1. When converting incoming visual geometry to logical we need to differentiate root box from participating inline level boxes.
e.g. a participating box's (visual) padding right always stretches the box to the logical top direction, while the root's padding right can either be a logical top or bottom constraint depending on the direction.
2. Always use line box's relative geometry flipping for inline content.

* LayoutTests/TestExpectations: These never worked (we were just lucky)
* LayoutTests/fast/inline/aligned-inline-box-decoration-in-vertical-writing-mode-expected.html: Added.
* LayoutTests/fast/inline/aligned-inline-box-decoration-in-vertical-writing-mode.html: Added.
* Source/WebCore/layout/formattingContexts/inline/display/InlineDisplayContentBuilder.cpp:
(WebCore::Layout::InlineDisplayContentBuilder::processNonBidiContent):
(WebCore::Layout::InlineDisplayContentBuilder::adjustVisualGeometryForDisplayBox):

Canonical link: https://commits.webkit.org/262855@main
  • Loading branch information
alanbaradlay committed Apr 12, 2023
1 parent 304640c commit 1612e71
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 36 deletions.
3 changes: 3 additions & 0 deletions LayoutTests/TestExpectations
Expand Up @@ -5272,6 +5272,9 @@ imported/w3c/web-platform-tests/css/css-writing-modes/float-lft-orthog-htb-in-vl
# Disagreeing with the expected result (content should not wrap).
webkit.org/b/245080 fast/text/whitespace/nowrap-clear-float.html [ Failure ]

webkit.org/b/255255 fast/text/text-underline-vertical-first-line-decoration.html [ ImageOnlyFailure ]
webkit.org/b/255287 fast/block/lineboxcontain/inline-box-vertical.html [ Failure ]

# This is marked as passing on WK2.
fast/canvas/large-getImageData.html [ Skip ]

Expand Down
@@ -0,0 +1,16 @@
<style>
div {
writing-mode: vertical-lr;
font-size: 50px;
font-family: Ahem;
white-space: pre;
}
span {
background-color: green;
color: blue;
}
</style>
<div><span>some </span><br>
<span>text</span></span>
<span>some </span><br>
<span>text</span></span></div>
@@ -0,0 +1,14 @@
<style>
div {
writing-mode: vertical-lr;
font-size: 50px;
font-family: Ahem;
}
span {
background-color: green;
color: blue;
}
</style>
<!-- PASS if content and background positions match -->
<div><span>some<span style="vertical-align: 100px">text</span></span>
<div><span style="unicode-bidi: isolate">some<span style="vertical-align: 100px">text</span></span>
Expand Up @@ -434,9 +434,7 @@ void InlineDisplayContentBuilder::processNonBidiContent(const LineBuilder::LineC
auto& layoutBox = lineRun.layoutBox();

auto visualRectRelativeToRoot = [&](auto logicalRect) {
auto isContentRun = !lineRun.isInlineBoxStart() && !lineRun.isInlineBoxEnd() && !lineRun.isLineSpanningInlineBoxStart();
auto visualRect = isContentRun ? flipLogicalRectToVisualForWritingModeWithinLine(logicalRect, lineBox.logicalRect(), writingMode)
: flipLogicalRectToVisualForWritingMode(logicalRect, writingMode);
auto visualRect = flipLogicalRectToVisualForWritingModeWithinLine(logicalRect, lineBox.logicalRect(), writingMode);
visualRect.moveBy(contentStartInVisualOrder);
return visualRect;
};
Expand Down Expand Up @@ -560,7 +558,7 @@ struct IsFirstLastIndex {
std::optional<size_t> last;
};
using IsFirstLastIndexesMap = HashMap<const Box*, IsFirstLastIndex>;
void InlineDisplayContentBuilder::adjustVisualGeometryForDisplayBox(size_t displayBoxNodeIndex, InlineLayoutUnit& contentRightInInlineDirectionVisualOrder, InlineLayoutUnit lineBoxLogicalTop, const DisplayBoxTree& displayBoxTree, InlineDisplay::Boxes& boxes, const LineBox& lineBox, const IsFirstLastIndexesMap& isFirstLastIndexesMap)
void InlineDisplayContentBuilder::adjustVisualGeometryForDisplayBox(size_t displayBoxNodeIndex, InlineLayoutUnit& contentRightInInlineDirectionVisualOrder, InlineLayoutUnit lineBoxLogicalTop, const DisplayBoxTree& displayBoxTree, InlineDisplay::Boxes& boxes, const InlineDisplay::Line& displayLine, const LineBox& lineBox, const IsFirstLastIndexesMap& isFirstLastIndexesMap)
{
auto writingMode = root().style().writingMode();
auto isHorizontalWritingMode = WebCore::isHorizontalWritingMode(writingMode);
Expand Down Expand Up @@ -597,7 +595,8 @@ void InlineDisplayContentBuilder::adjustVisualGeometryForDisplayBox(size_t displ
auto isLastBox = isFirstLastIndexes.last && *isFirstLastIndexes.last == displayBoxNodeIndex;
auto beforeInlineBoxContent = [&] {
auto logicalRect = lineBox.logicalBorderBoxForInlineBox(layoutBox, boxGeometry);
auto visualRect = flipLogicalRectToVisualForWritingMode({ lineBoxLogicalTop + logicalRect.top(), contentRightInInlineDirectionVisualOrder, { }, logicalRect.height() }, writingMode);
auto visualRect = flipLogicalRectToVisualForWritingModeWithinLine({ logicalRect.top(), contentRightInInlineDirectionVisualOrder, { }, logicalRect.height() }, lineBox.logicalRect(), writingMode);
isHorizontalWritingMode ? visualRect.moveVertically(displayLine.top()) : visualRect.moveHorizontally(displayLine.left());
displayBox.setRect(visualRect, visualRect);

auto shouldApplyLeftSide = (isLeftToRightDirection && isFirstBox) || (!isLeftToRightDirection && isLastBox);
Expand All @@ -611,7 +610,7 @@ void InlineDisplayContentBuilder::adjustVisualGeometryForDisplayBox(size_t displ
beforeInlineBoxContent();

for (auto childDisplayBoxNodeIndex : displayBoxTree.at(displayBoxNodeIndex).children)
adjustVisualGeometryForDisplayBox(childDisplayBoxNodeIndex, contentRightInInlineDirectionVisualOrder, lineBoxLogicalTop, displayBoxTree, boxes, lineBox, isFirstLastIndexesMap);
adjustVisualGeometryForDisplayBox(childDisplayBoxNodeIndex, contentRightInInlineDirectionVisualOrder, lineBoxLogicalTop, displayBoxTree, boxes, displayLine, lineBox, isFirstLastIndexesMap);

auto afterInlineBoxContent = [&] {
auto shouldApplyRightSide = (isLeftToRightDirection && isLastBox) || (!isLeftToRightDirection && isFirstBox);
Expand Down Expand Up @@ -778,7 +777,7 @@ void InlineDisplayContentBuilder::processBidiContent(const LineBuilder::LineCont
auto contentRightInInlineDirectionVisualOrder = contentStartInInlineDirectionVisualOrder;

for (auto childDisplayBoxNodeIndex : displayBoxTree.root().children)
adjustVisualGeometryForDisplayBox(childDisplayBoxNodeIndex, contentRightInInlineDirectionVisualOrder, lineLogicalTop, displayBoxTree, boxes, lineBox, isFirstLastIndexesMap);
adjustVisualGeometryForDisplayBox(childDisplayBoxNodeIndex, contentRightInInlineDirectionVisualOrder, lineLogicalTop, displayBoxTree, boxes, displayLine, lineBox, isFirstLastIndexesMap);
};
adjustVisualGeometryWithInlineBoxes();
};
Expand Down Expand Up @@ -926,22 +925,6 @@ InlineRect InlineDisplayContentBuilder::flipLogicalRectToVisualForWritingModeWit
return logicalRect;
}

InlineRect InlineDisplayContentBuilder::flipLogicalRectToVisualForWritingMode(const InlineRect& logicalRect, WritingMode writingMode) const
{
switch (writingMode) {
case WritingMode::TopToBottom:
return logicalRect;
case WritingMode::LeftToRight:
case WritingMode::RightToLeft:
// See InlineFormattingGeometry for more info.
return { logicalRect.left(), logicalRect.top(), logicalRect.height(), logicalRect.width() };
default:
ASSERT_NOT_REACHED();
break;
}
return logicalRect;
}

InlineRect InlineDisplayContentBuilder::flipRootInlineBoxRectToVisualForWritingMode(const InlineRect& rootInlineBoxLogicalRect, const InlineDisplay::Line& displayLine, WritingMode writingMode) const
{
switch (writingMode) {
Expand Down
Expand Up @@ -64,11 +64,10 @@ class InlineDisplayContentBuilder {
void appendInlineDisplayBoxAtBidiBoundary(const Box&, InlineDisplay::Boxes&);

void setInlineBoxGeometry(const Box&, const InlineRect&, bool isFirstInlineBoxFragment);
void adjustVisualGeometryForDisplayBox(size_t displayBoxNodeIndex, InlineLayoutUnit& accumulatedOffset, InlineLayoutUnit lineBoxLogicalTop, const DisplayBoxTree&, InlineDisplay::Boxes&, const LineBox&, const HashMap<const Box*, IsFirstLastIndex>&);
void adjustVisualGeometryForDisplayBox(size_t displayBoxNodeIndex, InlineLayoutUnit& accumulatedOffset, InlineLayoutUnit lineBoxLogicalTop, const DisplayBoxTree&, InlineDisplay::Boxes&, const InlineDisplay::Line&, const LineBox&, const HashMap<const Box*, IsFirstLastIndex>&);
size_t ensureDisplayBoxForContainer(const ElementBox&, DisplayBoxTree&, AncestorStack&, InlineDisplay::Boxes&);

InlineRect flipLogicalRectToVisualForWritingModeWithinLine(const InlineRect& logicalRect, const InlineRect& lineLogicalRect, WritingMode) const;
InlineRect flipLogicalRectToVisualForWritingMode(const InlineRect& logicalRect, WritingMode) const;
InlineRect flipRootInlineBoxRectToVisualForWritingMode(const InlineRect& rootInlineBoxLogicalRect, const InlineDisplay::Line&, WritingMode) const;
void setLeftForWritingMode(InlineDisplay::Box&, InlineLayoutUnit logicalRight, WritingMode) const;
void setRightForWritingMode(InlineDisplay::Box&, InlineLayoutUnit logicalRight, WritingMode) const;
Expand Down
Expand Up @@ -308,7 +308,8 @@ static inline Layout::BoxGeometry::VerticalMargin verticalLogicalMargin(const Re
}
}

static inline Layout::Edges logicalBorder(const RenderBoxModelObject& renderer, bool isLeftToRightInlineDirection, WritingMode writingMode, bool retainBorderStart = true, bool retainBorderEnd = true)
enum class IsPartOfFormattingContext : bool { No, Yes };
static inline Layout::Edges logicalBorder(const RenderBoxModelObject& renderer, bool isLeftToRightInlineDirection, WritingMode writingMode, IsPartOfFormattingContext isPartOfFormattingContext = IsPartOfFormattingContext::No, bool retainBorderStart = true, bool retainBorderEnd = true)
{
auto borderLeft = renderer.borderLeft();
auto borderRight = renderer.borderRight();
Expand All @@ -323,12 +324,13 @@ static inline Layout::Edges logicalBorder(const RenderBoxModelObject& renderer,

auto borderLogicalLeft = retainBorderStart ? isLeftToRightInlineDirection ? borderTop : borderBottom : 0_lu;
auto borderLogicalRight = retainBorderEnd ? isLeftToRightInlineDirection ? borderBottom : borderTop : 0_lu;
auto borderLogicalTop = writingMode == WritingMode::LeftToRight ? borderLeft : borderRight;
auto borderLogicalBottom = writingMode == WritingMode::LeftToRight ? borderRight : borderLeft;
// For boxes inside the formatting context, right border (padding) always points up, while when converting the formatting context root's border (padding) the directionality matters.
auto borderLogicalTop = isPartOfFormattingContext == IsPartOfFormattingContext::Yes ? borderRight : writingMode == WritingMode::LeftToRight ? borderLeft : borderRight;
auto borderLogicalBottom = isPartOfFormattingContext == IsPartOfFormattingContext::Yes ? borderLeft : writingMode == WritingMode::LeftToRight ? borderRight : borderLeft;
return { { borderLogicalLeft, borderLogicalRight }, { borderLogicalTop, borderLogicalBottom } };
}

static inline Layout::Edges logicalPadding(const RenderBoxModelObject& renderer, bool isLeftToRightInlineDirection, WritingMode writingMode, bool retainPaddingStart = true, bool retainPaddingEnd = true)
static inline Layout::Edges logicalPadding(const RenderBoxModelObject& renderer, bool isLeftToRightInlineDirection, WritingMode writingMode, IsPartOfFormattingContext isPartOfFormattingContext = IsPartOfFormattingContext::No, bool retainPaddingStart = true, bool retainPaddingEnd = true)
{
auto paddingLeft = renderer.paddingLeft();
auto paddingRight = renderer.paddingRight();
Expand All @@ -343,15 +345,16 @@ static inline Layout::Edges logicalPadding(const RenderBoxModelObject& renderer,

auto paddingLogicalLeft = retainPaddingStart ? isLeftToRightInlineDirection ? paddingTop : paddingBottom : 0_lu;
auto paddingLogicalRight = retainPaddingEnd ? isLeftToRightInlineDirection ? paddingBottom : paddingTop : 0_lu;
auto paddingLogicalTop = writingMode == WritingMode::LeftToRight ? paddingLeft : paddingRight;
auto paddingLogicalBottom = writingMode == WritingMode::LeftToRight ? paddingRight : paddingLeft;
// For boxes inside the formatting context, right padding (border) always points up, while when converting the formatting context root's padding (border) the directionality matters.
auto paddingLogicalTop = isPartOfFormattingContext == IsPartOfFormattingContext::Yes ? paddingRight : writingMode == WritingMode::LeftToRight ? paddingLeft : paddingRight;
auto paddingLogicalBottom = isPartOfFormattingContext == IsPartOfFormattingContext::Yes ? paddingLeft : writingMode == WritingMode::LeftToRight ? paddingRight : paddingLeft;
return { { paddingLogicalLeft, paddingLogicalRight }, { paddingLogicalTop, paddingLogicalBottom } };
}

static inline LayoutSize scrollbarLogicalSize(const RenderBox& renderer)
{
// Scrollbars eat into the padding box area. They never stretch the border box but they may shrink the padding box.
// In legacy render tree, RenderBox::contentWidth/contentHeight values are adjusted to accomodate the scrollbar width/height.
// In legacy render tree, RenderBox::contentWidth/contentHeight values are adjusted to accommodate the scrollbar width/height.
// e.g. <div style="width: 10px; overflow: scroll;">content</div>, RenderBox::contentWidth() won't be returning the value of 10px but instead 0px (10px - 15px).
auto horizontalSpaceReservedForScrollbar = std::max(0_lu, renderer.paddingBoxRectIncludingScrollbar().width() - renderer.paddingBoxWidth());
auto verticalSpaceReservedForScrollbar = std::max(0_lu, renderer.paddingBoxRectIncludingScrollbar().height() - renderer.paddingBoxHeight());
Expand Down Expand Up @@ -453,8 +456,8 @@ void LineLayout::updateInlineBoxDimensions(const RenderInline& renderInline)

boxGeometry.setHorizontalMargin(horizontalLogicalMargin(renderInline, isLeftToRightInlineDirection, writingMode == WritingMode::TopToBottom, !shouldNotRetainBorderPaddingAndMarginStart, !shouldNotRetainBorderPaddingAndMarginEnd));
boxGeometry.setVerticalMargin(verticalLogicalMargin(renderInline, writingMode));
boxGeometry.setBorder(logicalBorder(renderInline, isLeftToRightInlineDirection, writingMode, !shouldNotRetainBorderPaddingAndMarginStart, !shouldNotRetainBorderPaddingAndMarginEnd));
boxGeometry.setPadding(logicalPadding(renderInline, isLeftToRightInlineDirection, writingMode, !shouldNotRetainBorderPaddingAndMarginStart, !shouldNotRetainBorderPaddingAndMarginEnd));
boxGeometry.setBorder(logicalBorder(renderInline, isLeftToRightInlineDirection, writingMode, IsPartOfFormattingContext::Yes, !shouldNotRetainBorderPaddingAndMarginStart, !shouldNotRetainBorderPaddingAndMarginEnd));
boxGeometry.setPadding(logicalPadding(renderInline, isLeftToRightInlineDirection, writingMode, IsPartOfFormattingContext::Yes, !shouldNotRetainBorderPaddingAndMarginStart, !shouldNotRetainBorderPaddingAndMarginEnd));
}

void LineLayout::updateInlineContentDimensions()
Expand Down Expand Up @@ -701,8 +704,8 @@ void LineLayout::updateInlineContentConstraints()
auto isLeftToRightInlineDirection = flow.style().isLeftToRightDirection();
auto writingMode = flow.style().writingMode();

auto padding = logicalPadding(flow, isLeftToRightInlineDirection, writingMode);
auto border = logicalBorder(flow, isLeftToRightInlineDirection, writingMode);
auto padding = logicalPadding(flow, isLeftToRightInlineDirection, writingMode, IsPartOfFormattingContext::No);
auto border = logicalBorder(flow, isLeftToRightInlineDirection, writingMode, IsPartOfFormattingContext::No);
auto scrollbarSize = scrollbarLogicalSize(flow);

auto contentBoxWidth = WebCore::isHorizontalWritingMode(writingMode) ? flow.contentWidth() : flow.contentHeight();
Expand Down

0 comments on commit 1612e71

Please sign in to comment.