Commit c126b7af authored by Morten Stenshorne's avatar Morten Stenshorne Committed by Commit Bot

[LayoutNG] Implement -webkit-margin-collapse

-webkit-margin-collapse is a shorthand for -webkit-margin-before-collapse and
-webkit-margin-after-collapse (and there's also -webkit-margin-top-collapse and
-webkit-margin-bottom-collapse that will only work in horizontal writing modes).

These are all about controlling whether we should collapse block-direction
margins (default), separate them, or discard them completely.

If we separate margins, we'll separate (otherwise) adjoining margins before the
margin from the margin in question, AND separate the margin in question from
(otherwise) adjoining margins after it.

If we discard a margin, we'll discard all (otherwise) adjoining margins, both
before and after the margin in question.

There's no spec. This is based on the behavior in legacy Blink / WebKit.

Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_layout_ng
Change-Id: I91c869013d11a0ce9327908ce724d518a19209e8
Reviewed-on: https://chromium-review.googlesource.com/1111117
Commit-Queue: Morten Stenshorne <mstensho@chromium.org>
Reviewed-by: default avatarIan Kilpatrick <ikilpatrick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#569892}
parent 7e5b33bd
......@@ -350,9 +350,6 @@ crbug.com/810370 fast/block/float/overhanging-float-remove-from-fixed-position-b
crbug.com/810370 fast/block/float/overhanging-float-remove-from-fixed-position-block2.html [ Failure ]
crbug.com/591099 fast/block/float/overlapping-floats-paint-hittest-order-1.html [ Failure ]
crbug.com/591099 fast/block/line-layout/floats-do-not-fit-on-line.html [ Failure ]
crbug.com/591099 fast/block/margin-collapse/webkit-margin-collapse-container.html [ Failure ]
crbug.com/591099 fast/block/margin-collapse/webkit-margin-collapse-separate-position.html [ Failure ]
crbug.com/591099 fast/block/margin-collapse/webkit-margin-collapse-siblings.html [ Failure ]
crbug.com/591099 fast/block/over-constrained-auto-margin.html [ Failure ]
crbug.com/591099 fast/block/positioning/complex-positioned-movement-inline-ancestor.html [ Failure ]
crbug.com/591099 fast/block/positioning/positioned-child-inside-relative-positioned-anonymous-block.html [ Failure ]
......
......@@ -362,11 +362,6 @@ crbug.com/635619 [ Mac ] virtual/layout_ng/fast/block/float/width-update-after-c
### virtual/layout_ng/fast/block/margin-collapse
crbug.com/635619 [ Mac ] virtual/layout_ng/fast/block/margin-collapse/103.html [ Failure ]
### virtual/layout_ng/fast/block/margin-collapse
crbug.com/635619 virtual/layout_ng/fast/block/margin-collapse/webkit-margin-collapse-container.html [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/margin-collapse/webkit-margin-collapse-separate-position.html [ Failure ]
crbug.com/635619 virtual/layout_ng/fast/block/margin-collapse/webkit-margin-collapse-siblings.html [ Failure ]
### virtual/layout_ng/overflow
crbug.com/724701 virtual/layout_ng/overflow/overflow-basic-004.html [ Failure ]
......
......@@ -9,10 +9,14 @@
namespace blink {
LayoutUnit NGMarginStrut::Sum() const {
if (discard_margins)
return LayoutUnit();
return std::max(quirky_positive_margin, positive_margin) + negative_margin;
}
LayoutUnit NGMarginStrut::QuirkyContainerSum() const {
if (discard_margins)
return LayoutUnit();
return positive_margin + negative_margin;
}
......@@ -34,6 +38,8 @@ void NGMarginStrut::Append(const LayoutUnit& value, bool is_quirky) {
}
bool NGMarginStrut::IsEmpty() const {
if (discard_margins)
return true;
return positive_margin == LayoutUnit() && negative_margin == LayoutUnit() &&
quirky_positive_margin == LayoutUnit();
}
......
......@@ -24,6 +24,10 @@ struct CORE_EXPORT NGMarginStrut {
// See comment inside NGBlockLayoutAlgorithm for when this occurs.
bool is_quirky_container_start = false;
// If set, we will discard all adjoining margins, which is the
// effect of -webkit-margin-collapse:discard.
bool discard_margins = false;
// Appends negative or positive value to the current margin strut.
void Append(const LayoutUnit& value, bool is_quirky);
......
......@@ -154,6 +154,27 @@ scoped_refptr<NGConstraintSpace> CreateExtrinsicConstraintSpace(
.ToConstraintSpace(child.Style().GetWritingMode());
}
// Stop margin collapsing on one side of a block when
// -webkit-margin-{after,before}-collapse is something other than 'collapse'
// (the initial value)
void StopMarginCollapsing(EMarginCollapse collapse_value,
LayoutUnit this_margin,
LayoutUnit* logical_block_offset,
NGMarginStrut* margin_strut) {
DCHECK_NE(collapse_value, EMarginCollapse::kCollapse);
if (collapse_value == EMarginCollapse::kSeparate) {
// Separate margins between previously adjoining margins and this margin,
// AND between this margin and adjoining margins to come.
*logical_block_offset += margin_strut->Sum() + this_margin;
*margin_strut = NGMarginStrut();
return;
}
DCHECK_EQ(collapse_value, EMarginCollapse::kDiscard);
// Discard previously adjoining margins, this margin AND all adjoining margins
// to come, so that the sum becomes 0.
margin_strut->discard_margins = true;
}
} // namespace
NGBlockLayoutAlgorithm::NGBlockLayoutAlgorithm(NGBlockNode node,
......@@ -384,14 +405,22 @@ scoped_refptr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() {
LayoutUnit(), ConstraintSpace().MarginStrut(),
/* empty_block_affected_by_clearance */ false};
// Margins collapsing: Do not collapse margins between parent and its child if
// there is border/padding between them. Then we can and must resolve the BFC
// offset now. Also, if this is a new formatting context, or if we're resuming
// layout from a break token, we need to resolve the BFC offset now. Margin
// struts cannot pass from one fragment to another if they are generated by
// the same block; they must be dealt with at the first fragment.
// Do not collapse margins between parent and its child if:
//
// A: There is border/padding between them.
// B: This is a new formatting context
// C: We're resuming layout from a break token. Margin struts cannot pass from
// one fragment to another if they are generated by the same block; they
// must be dealt with at the first fragment.
// D: We're forced to stop margin collapsing by a CSS property
//
// In all those cases we can and must resolve the BFC offset now.
if (border_scrollbar_padding_.block_start || is_resuming_ ||
ConstraintSpace().IsNewFormattingContext()) {
ConstraintSpace().IsNewFormattingContext() ||
Style().MarginBeforeCollapse() != EMarginCollapse::kCollapse) {
bool discard_subsequent_margins =
previous_inflow_position.margin_strut.discard_margins &&
!border_scrollbar_padding_.block_start;
if (!ResolveBfcOffset(&previous_inflow_position)) {
// There should be no preceding content that depends on the BFC offset of
// a new formatting context block, and likewise when resuming from a break
......@@ -402,6 +431,13 @@ scoped_refptr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() {
}
// Move to the content edge. This is where the first child should be placed.
previous_inflow_position.logical_block_offset = content_edge;
// If we resolved the BFC offset now, the margin strut has been reset. If
// margins are to be discarded, and this box would otherwise have adjoining
// margins between its own margin and those subsequent content, we need to
// make sure subsequent content discard theirs.
if (discard_subsequent_margins)
previous_inflow_position.margin_strut.discard_margins = true;
}
#if DCHECK_IS_ON()
......@@ -1230,14 +1266,25 @@ NGInflowChildData NGBlockLayoutAlgorithm::ComputeChildData(
// Non empty border/padding, and new FC use cases are handled inside of the
// child's layout
NGMarginStrut margin_strut = previous_inflow_position.margin_strut;
margin_strut.Append(margins.block_start,
child.Style().HasMarginBeforeQuirk());
LayoutUnit logical_block_offset =
previous_inflow_position.logical_block_offset;
if (child.Style().MarginBeforeCollapse() != EMarginCollapse::kCollapse) {
// Stop margin collapsing on the block-start side of the child.
StopMarginCollapsing(child.Style().MarginBeforeCollapse(),
margins.block_start, &logical_block_offset,
&margin_strut);
} else {
margin_strut.Append(margins.block_start,
child.Style().HasMarginBeforeQuirk());
}
NGBfcOffset child_bfc_offset = {
ConstraintSpace().BfcOffset().line_offset +
border_scrollbar_padding_.LineLeft(ConstraintSpace().Direction()) +
margins.LineLeft(ConstraintSpace().Direction()),
BfcBlockOffset() + previous_inflow_position.logical_block_offset};
BfcBlockOffset() + logical_block_offset};
return {child_bfc_offset, margin_strut, margins, force_clearance};
}
......@@ -1315,8 +1362,16 @@ NGPreviousInflowPosition NGBlockLayoutAlgorithm::ComputeInflowPosition(
}
NGMarginStrut margin_strut = layout_result.EndMarginStrut();
margin_strut.Append(child_data.margins.block_end,
child.Style().HasMarginAfterQuirk());
if (child.Style().MarginAfterCollapse() != EMarginCollapse::kCollapse) {
// Stop margin collapsing on the block-end side of the child.
StopMarginCollapsing(child.Style().MarginAfterCollapse(),
child_data.margins.block_end, &logical_block_offset,
&margin_strut);
} else {
margin_strut.Append(child_data.margins.block_end,
child.Style().HasMarginAfterQuirk());
}
// This flag is subtle, but in order to determine our size correctly we need
// to check if our last child is an empty block, and it was affected by
......
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