Commit f82f5505 authored by Ian Kilpatrick's avatar Ian Kilpatrick Committed by Commit Bot

[LayoutNG] Fix inline-level OOFs inside a block-level context.

Previously we considered text-align for inline-level OOFs inside a
block-level context. But we also needed to avoid floats.

This is surprisingly consistent across browsers! However no tests. :(
So fixed and added tests!

Most of the complexity in this patch is making sure the
logical->line->logical coordinate transforms are done correctly.

Bug: 933996
Change-Id: I78207f8b7cba62e9d6f48f087fd5202178b42910
Reviewed-on: https://chromium-review.googlesource.com/c/1480071Reviewed-by: default avatarAleks Totic <atotic@chromium.org>
Reviewed-by: default avatarMorten Stenshorne <mstensho@chromium.org>
Commit-Queue: Ian Kilpatrick <ikilpatrick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#634522}
parent 5be4504e
......@@ -6,10 +6,6 @@
namespace blink {
bool NGBfcRect::IsEmpty() const {
return start_offset == end_offset;
}
bool NGBfcRect::operator==(const NGBfcRect& other) const {
return start_offset == other.start_offset && end_offset == other.end_offset;
}
......
......@@ -21,17 +21,11 @@ struct CORE_EXPORT NGBfcRect {
DCHECK_GE(end_offset.block_offset, start_offset.block_offset);
}
bool IsEmpty() const;
LayoutUnit LineStartOffset() const { return start_offset.line_offset; }
LayoutUnit LineEndOffset() const { return end_offset.line_offset; }
LayoutUnit BlockStartOffset() const { return start_offset.block_offset; }
LayoutUnit BlockEndOffset() const { return end_offset.block_offset; }
NGBfcOffset LineEndBlockStartOffset() const {
return {LineEndOffset(), BlockStartOffset()};
}
LayoutUnit BlockSize() const {
if (end_offset.block_offset == LayoutUnit::Max())
return LayoutUnit::Max();
......@@ -44,7 +38,6 @@ struct CORE_EXPORT NGBfcRect {
return end_offset.line_offset - start_offset.line_offset;
}
NGLogicalSize Size() const { return {InlineSize(), BlockSize()}; }
bool operator==(const NGBfcRect& other) const;
bool operator!=(const NGBfcRect& other) const { return !(*this == other); }
......
......@@ -109,9 +109,9 @@ inline bool IsEmptyBlock(const NGConstraintSpace& child_space,
return IsEmptyBlock(child_space.IsNewFormattingContext(), layout_result);
}
LayoutUnit LogicalFromBfcLineOffset(const NGFragment& fragment,
LayoutUnit child_bfc_line_offset,
LayoutUnit LogicalFromBfcLineOffset(LayoutUnit child_bfc_line_offset,
LayoutUnit parent_bfc_line_offset,
LayoutUnit child_inline_size,
LayoutUnit parent_inline_size,
TextDirection direction) {
// We need to respect the current text direction to calculate the logical
......@@ -122,19 +122,19 @@ LayoutUnit LogicalFromBfcLineOffset(const NGFragment& fragment,
LayoutUnit inline_offset =
direction == TextDirection::kLtr
? relative_line_offset
: parent_inline_size - relative_line_offset - fragment.InlineSize();
: parent_inline_size - relative_line_offset - child_inline_size;
return inline_offset;
}
NGLogicalOffset LogicalFromBfcOffsets(const NGFragment& fragment,
const NGBfcOffset& child_bfc_offset,
NGLogicalOffset LogicalFromBfcOffsets(const NGBfcOffset& child_bfc_offset,
const NGBfcOffset& parent_bfc_offset,
LayoutUnit child_inline_size,
LayoutUnit parent_inline_size,
TextDirection direction) {
LayoutUnit inline_offset = LogicalFromBfcLineOffset(
fragment, child_bfc_offset.line_offset, parent_bfc_offset.line_offset,
parent_inline_size, direction);
child_bfc_offset.line_offset, parent_bfc_offset.line_offset,
child_inline_size, parent_inline_size, direction);
return {inline_offset,
child_bfc_offset.block_offset - parent_bfc_offset.block_offset};
......@@ -337,13 +337,13 @@ NGLogicalOffset NGBlockLayoutAlgorithm::CalculateLogicalOffset(
if (child_bfc_block_offset) {
return LogicalFromBfcOffsets(
fragment, {child_bfc_line_offset, *child_bfc_block_offset},
ContainerBfcOffset(), inline_size, direction);
{child_bfc_line_offset, *child_bfc_block_offset}, ContainerBfcOffset(),
fragment.InlineSize(), inline_size, direction);
}
LayoutUnit inline_offset = LogicalFromBfcLineOffset(
fragment, child_bfc_line_offset, container_builder_.BfcLineOffset(),
inline_size, direction);
child_bfc_line_offset, container_builder_.BfcLineOffset(),
fragment.InlineSize(), inline_size, direction);
// If we've reached here, both the child and the current layout don't have a
// BFC block offset yet. Children in this situation are always placed at a
......@@ -814,17 +814,8 @@ void NGBlockLayoutAlgorithm::HandleOutOfFlowPositioned(
const NGPreviousInflowPosition& previous_inflow_position,
NGBlockNode child) {
const ComputedStyle& child_style = child.Style();
LayoutUnit inline_offset = border_scrollbar_padding_.inline_start;
if (child_style.IsOriginalDisplayInlineType()) {
// If this out-of-flow child is inline type, its static position should
// honor the 'text-align' property.
inline_offset +=
InlineOffsetForTextAlign(Style(), child_available_size_.inline_size);
}
// TODO(ikilpatrick): Determine which of the child's margins need to be
// included for the static position.
NGLogicalOffset offset = {inline_offset,
NGLogicalOffset offset = {border_scrollbar_padding_.inline_start,
previous_inflow_position.logical_block_offset};
// We only include the margin strut in the OOF static-position if we know we
......@@ -832,6 +823,37 @@ void NGBlockLayoutAlgorithm::HandleOutOfFlowPositioned(
if (container_builder_.BfcBlockOffset())
offset.block_offset += previous_inflow_position.margin_strut.Sum();
if (child_style.IsOriginalDisplayInlineType()) {
// OOF-positioned nodes which were initially inline-level, however are in a
// block-level context, pretend they are in an inline-level context. E.g.
// they avoid floats, and respect text-align.
const TextDirection direction = ConstraintSpace().Direction();
LayoutUnit child_origin_line_offset =
container_builder_.BfcLineOffset() +
border_scrollbar_padding_.LineLeft(direction);
// Find a layout opportunity, this is where we would have placed a
// zero-sized line.
NGLayoutOpportunity opportunity = exclusion_space_.FindLayoutOpportunity(
{child_origin_line_offset, BfcBlockOffset() + offset.block_offset},
child_available_size_.inline_size, /* minimum_size */ NGLogicalSize());
LayoutUnit child_line_offset = IsLtr(direction)
? opportunity.rect.LineStartOffset()
: opportunity.rect.LineEndOffset();
// Convert back to the logical coordinate system. As the conversion is on
// an OOF-positioned node, we pretent it has zero inline-size.
offset.inline_offset = LogicalFromBfcLineOffset(
child_line_offset, container_builder_.BfcLineOffset(),
/* child_inline_size */ LayoutUnit(),
container_builder_.Size().inline_size, direction);
// Adjust for text alignment, within the layout opportunity.
offset.inline_offset +=
InlineOffsetForTextAlign(Style(), opportunity.rect.InlineSize());
}
container_builder_.AddOutOfFlowChildCandidate(child, offset);
}
......@@ -1058,7 +1080,7 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext(
opportunity.rect.start_offset.block_offset);
NGLogicalOffset logical_offset = LogicalFromBfcOffsets(
fragment, child_bfc_offset, ContainerBfcOffset(),
child_bfc_offset, ContainerBfcOffset(), fragment.InlineSize(),
container_builder_.Size().inline_size, ConstraintSpace().Direction());
if (ConstraintSpace().HasBlockFragmentation()) {
......@@ -2280,7 +2302,7 @@ void NGBlockLayoutAlgorithm::PositionPendingFloats(
*positioned_float.layout_result->PhysicalFragment());
NGLogicalOffset logical_offset = LogicalFromBfcOffsets(
child_fragment, positioned_float.bfc_offset, bfc_offset,
positioned_float.bfc_offset, bfc_offset, child_fragment.InlineSize(),
container_builder_.Size().inline_size, ConstraintSpace().Direction());
container_builder_.AddChild(*positioned_float.layout_result,
......
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width" />
<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="Tests the static position of inline-level absolute-positioned elements, with combinations of float, direction, and text-align." />
<style>
#container { position: relative; background: red; width: 100px; height: 100px; }
#container > div { background: green; }
#inflow { height: 50px; }
#float { float: left; width: 50px; height: 50px; }
#abs { display: inline; position: absolute; width: 50px; height: 50px; }
</style>
<p>Test passes if there is a filled green square.</p>
<div id=container style="direction: ltr; text-align: left;">
<div id=inflow></div>
<div id=float style="float: left;"></div>
<div id=abs style="transform: translateX(0%);"></div>
</div>
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width" />
<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="Tests the static position of inline-level absolute-positioned elements, with combinations of float, direction, and text-align." />
<style>
#container { position: relative; background: red; width: 100px; height: 100px; }
#container > div { background: green; }
#inflow { height: 50px; }
#float { float: left; width: 50px; height: 50px; }
#abs { display: inline; position: absolute; width: 50px; height: 50px; }
</style>
<p>Test passes if there is a filled green square.</p>
<div id=container style="direction: ltr; text-align: center;">
<div id=inflow></div>
<div id=float style="float: left;"></div>
<div id=abs style="transform: translateX(-50%);"></div>
</div>
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width" />
<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="Tests the static position of inline-level absolute-positioned elements, with combinations of float, direction, and text-align." />
<style>
#container { position: relative; background: red; width: 100px; height: 100px; }
#container > div { background: green; }
#inflow { height: 50px; }
#float { float: left; width: 50px; height: 50px; }
#abs { display: inline; position: absolute; width: 50px; height: 50px; }
</style>
<p>Test passes if there is a filled green square.</p>
<div id=container style="direction: ltr; text-align: right;">
<div id=inflow></div>
<div id=float style="float: left;"></div>
<div id=abs style="transform: translateX(-100%);"></div>
</div>
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width" />
<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="Tests the static position of inline-level absolute-positioned elements, with combinations of float, direction, and text-align." />
<style>
#container { position: relative; background: red; width: 100px; height: 100px; }
#container > div { background: green; }
#inflow { height: 50px; }
#float { float: left; width: 50px; height: 50px; }
#abs { display: inline; position: absolute; width: 50px; height: 50px; }
</style>
<p>Test passes if there is a filled green square.</p>
<div id=container style="direction: ltr; text-align: left;">
<div id=inflow></div>
<div id=float style="float: right;"></div>
<div id=abs style="transform: translateX(0%);"></div>
</div>
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width" />
<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="Tests the static position of inline-level absolute-positioned elements, with combinations of float, direction, and text-align." />
<style>
#container { position: relative; background: red; width: 100px; height: 100px; }
#container > div { background: green; }
#inflow { height: 50px; }
#float { float: left; width: 50px; height: 50px; }
#abs { display: inline; position: absolute; width: 50px; height: 50px; }
</style>
<p>Test passes if there is a filled green square.</p>
<div id=container style="direction: ltr; text-align: center;">
<div id=inflow></div>
<div id=float style="float: right;"></div>
<div id=abs style="transform: translateX(-50%);"></div>
</div>
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width" />
<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="Tests the static position of inline-level absolute-positioned elements, with combinations of float, direction, and text-align." />
<style>
#container { position: relative; background: red; width: 100px; height: 100px; }
#container > div { background: green; }
#inflow { height: 50px; }
#float { float: left; width: 50px; height: 50px; }
#abs { display: inline; position: absolute; width: 50px; height: 50px; }
</style>
<p>Test passes if there is a filled green square.</p>
<div id=container style="direction: ltr; text-align: right;">
<div id=inflow></div>
<div id=float style="float: right;"></div>
<div id=abs style="transform: translateX(-100%);"></div>
</div>
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width" />
<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="Tests the static position of inline-level absolute-positioned elements, with combinations of float, direction, and text-align." />
<style>
#container { position: relative; background: red; width: 100px; height: 100px; }
#container > div { background: green; }
#inflow { height: 50px; }
#float { float: left; width: 50px; height: 50px; }
#abs { display: inline; position: absolute; width: 50px; height: 50px; }
</style>
<p>Test passes if there is a filled green square.</p>
<div id=container style="direction: rtl; text-align: right;">
<div id=inflow></div>
<div id=float style="float: left;"></div>
<div id=abs style="transform: translateX(0%);"></div>
</div>
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width" />
<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="Tests the static position of inline-level absolute-positioned elements, with combinations of float, direction, and text-align." />
<style>
#container { position: relative; background: red; width: 100px; height: 100px; }
#container > div { background: green; }
#inflow { height: 50px; }
#float { float: left; width: 50px; height: 50px; }
#abs { display: inline; position: absolute; width: 50px; height: 50px; }
</style>
<p>Test passes if there is a filled green square.</p>
<div id=container style="direction: rtl; text-align: center;">
<div id=inflow></div>
<div id=float style="float: left;"></div>
<div id=abs style="transform: translateX(50%);"></div>
</div>
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width" />
<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="Tests the static position of inline-level absolute-positioned elements, with combinations of float, direction, and text-align." />
<style>
#container { position: relative; background: red; width: 100px; height: 100px; }
#container > div { background: green; }
#inflow { height: 50px; }
#float { float: left; width: 50px; height: 50px; }
#abs { display: inline; position: absolute; width: 50px; height: 50px; }
</style>
<p>Test passes if there is a filled green square.</p>
<div id=container style="direction: rtl; text-align: left;">
<div id=inflow></div>
<div id=float style="float: left;"></div>
<div id=abs style="transform: translateX(100%);"></div>
</div>
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width" />
<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="Tests the static position of inline-level absolute-positioned elements, with combinations of float, direction, and text-align." />
<style>
#container { position: relative; background: red; width: 100px; height: 100px; }
#container > div { background: green; }
#inflow { height: 50px; }
#float { float: left; width: 50px; height: 50px; }
#abs { display: inline; position: absolute; width: 50px; height: 50px; }
</style>
<p>Test passes if there is a filled green square.</p>
<div id=container style="direction: rtl; text-align: right;">
<div id=inflow></div>
<div id=float style="float: right;"></div>
<div id=abs style="transform: translateX(0%);"></div>
</div>
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width" />
<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="Tests the static position of inline-level absolute-positioned elements, with combinations of float, direction, and text-align." />
<style>
#container { position: relative; background: red; width: 100px; height: 100px; }
#container > div { background: green; }
#inflow { height: 50px; }
#float { float: left; width: 50px; height: 50px; }
#abs { display: inline; position: absolute; width: 50px; height: 50px; }
</style>
<p>Test passes if there is a filled green square.</p>
<div id=container style="direction: rtl; text-align: center;">
<div id=inflow></div>
<div id=float style="float: right;"></div>
<div id=abs style="transform: translateX(50%);"></div>
</div>
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width" />
<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
<meta name="assert" content="Tests the static position of inline-level absolute-positioned elements, with combinations of float, direction, and text-align." />
<style>
#container { position: relative; background: red; width: 100px; height: 100px; }
#container > div { background: green; }
#inflow { height: 50px; }
#float { float: left; width: 50px; height: 50px; }
#abs { display: inline; position: absolute; width: 50px; height: 50px; }
</style>
<p>Test passes if there is a filled green square.</p>
<div id=container style="direction: rtl; text-align: left;">
<div id=inflow></div>
<div id=float style="float: right;"></div>
<div id=abs style="transform: translateX(100%);"></div>
</div>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment