Skip to content

Commit

Permalink
Merge 4896: [block-in-inline] Fix when inserting inline in the middle…
Browse files Browse the repository at this point in the history
… of blocks

When inserting an inline child in the middle of blocks-in-
inline, the |LayoutObject| tree must be adjusted. For example:
```
LayoutInline
  LayoutBlockFlow (anonymous)
    LayoutBlockFlow DIV
    LayoutBlockFlow DIV
```
should become:
```
LayoutInline
  LayoutBlockFlow (anonymous)
    LayoutBlockFlow DIV
  LayoutInline
  LayoutBlockFlow (anonymous)
    LayoutBlockFlow DIV
```

To do this, this patch moves |SplitAnonymousBoxesAroundChild|
from |LayoutBox| to |LayoutBoxModelObject|, and changes it to
work with |LayoutInline|.

(cherry picked from commit 07d9345)

Bug: 1301982
Change-Id: I680748159d50826f152b0bf6c46d11bb33563c40
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3499975
Reviewed-by: Ian Kilpatrick <ikilpatrick@chromium.org>
Commit-Queue: Koji Ishii <kojii@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#978136}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3507084
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/branch-heads/4896@{#386}
Cr-Branched-From: 1f63ff4-refs/heads/main@{#972766}
  • Loading branch information
kojiishi authored and Chromium LUCI CQ committed Mar 8, 2022
1 parent 93806c7 commit f87440d
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 11 deletions.
16 changes: 11 additions & 5 deletions third_party/blink/renderer/core/layout/layout_box.cc
Expand Up @@ -8017,7 +8017,7 @@ bool LayoutBox::HasRelativeLogicalHeight() const {
StyleRef().LogicalMaxHeight().IsPercentOrCalc();
}

static void MarkBoxForRelayoutAfterSplit(LayoutBox* box) {
static void MarkBoxForRelayoutAfterSplit(LayoutBoxModelObject* box) {
// FIXME: The table code should handle that automatically. If not,
// we should fix it and remove the table part checks.
if (box->IsTable()) {
Expand All @@ -8044,7 +8044,8 @@ static void CollapseLoneAnonymousBlockChild(LayoutBox* parent,
parent_block_flow->CollapseAnonymousBlockChild(child_block_flow);
}

LayoutObject* LayoutBox::SplitAnonymousBoxesAroundChild(
// TODO(kojii): Move to `layout_box_model_object.cc`.
LayoutObject* LayoutBoxModelObject::SplitAnonymousBoxesAroundChild(
LayoutObject* before_child) {
NOT_DESTROYED();
LayoutBox* box_at_top_of_new_branch = nullptr;
Expand All @@ -8055,10 +8056,9 @@ LayoutObject* LayoutBox::SplitAnonymousBoxesAroundChild(
box_to_split->IsAnonymous()) {
// We have to split the parent box into two boxes and move children
// from |beforeChild| to end into the new post box.
LayoutBox* post_box =
box_to_split->CreateAnonymousBoxWithSameTypeAs(this);
LayoutBox* post_box = CreateAnonymousBoxToSplit(box_to_split);
post_box->SetChildrenInline(box_to_split->ChildrenInline());
auto* parent_box = To<LayoutBox>(box_to_split->Parent());
auto* parent_box = To<LayoutBoxModelObject>(box_to_split->Parent());
// We need to invalidate the |parentBox| before inserting the new node
// so that the table paint invalidation logic knows the structure is
// dirty. See for example LayoutTableCell:localVisualRect().
Expand Down Expand Up @@ -8098,6 +8098,12 @@ LayoutObject* LayoutBox::SplitAnonymousBoxesAroundChild(
return before_child;
}

LayoutBox* LayoutBoxModelObject::CreateAnonymousBoxToSplit(
const LayoutBox* box_to_split) const {
NOT_DESTROYED();
return box_to_split->CreateAnonymousBoxWithSameTypeAs(this);
}

LayoutUnit LayoutBox::OffsetFromLogicalTopOfFirstPage() const {
NOT_DESTROYED();
LayoutState* layout_state = View()->GetLayoutState();
Expand Down
2 changes: 0 additions & 2 deletions third_party/blink/renderer/core/layout/layout_box.h
Expand Up @@ -2143,8 +2143,6 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject {
LayoutUnit intrinsic_content_height,
LayoutUnit border_and_padding) const;

LayoutObject* SplitAnonymousBoxesAroundChild(LayoutObject* before_child);

virtual bool HitTestChildren(HitTestResult&,
const HitTestLocation&,
const PhysicalOffset& accumulated_offset,
Expand Down
Expand Up @@ -672,6 +672,10 @@ class CORE_EXPORT LayoutBoxModelObject : public LayoutObject {
LayoutObject* before_child,
bool full_remove_insert = false);

LayoutObject* SplitAnonymousBoxesAroundChild(LayoutObject* before_child);
virtual LayoutBox* CreateAnonymousBoxToSplit(
const LayoutBox* box_to_split) const;

private:
void QuadsInternal(Vector<gfx::QuadF>& quads,
MapCoordinatesFlags mode,
Expand Down
29 changes: 26 additions & 3 deletions third_party/blink/renderer/core/layout/layout_inline.cc
Expand Up @@ -206,7 +206,8 @@ void LayoutInline::UpdateFromStyle() {
SetHasReflection(false);
}

static LayoutObject* InFlowPositionedInlineAncestor(LayoutObject* p) {
static const LayoutObject* InFlowPositionedInlineAncestor(
const LayoutObject* p) {
while (p && p->IsLayoutInline()) {
if (p->IsInFlowPositioned())
return p;
Expand Down Expand Up @@ -575,6 +576,18 @@ void LayoutInline::AddChildIgnoringContinuation(LayoutObject* new_child,
return;
}

// If inserting an inline child before a block-in-inline, change
// |before_child| to the anonymous block. The anonymous block may need to be
// split if |before_child| is not the first child.
if (before_child && before_child->Parent() != this &&
RuntimeEnabledFeatures::LayoutNGBlockInInlineEnabled()) {
DCHECK(!ForceLegacyLayout());
DCHECK(before_child->Parent()->IsBlockInInline());
DCHECK(IsA<LayoutBlockFlow>(before_child->Parent()));
DCHECK_EQ(before_child->Parent()->Parent(), this);
before_child = SplitAnonymousBoxesAroundChild(before_child);
}

LayoutBoxModelObject::AddChild(new_child, before_child);

new_child->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
Expand Down Expand Up @@ -761,7 +774,7 @@ void LayoutInline::SplitFlow(LayoutObject* before_child,
}

LayoutBlockFlow* LayoutInline::CreateAnonymousContainerForBlockChildren(
bool split_flow) {
bool split_flow) const {
NOT_DESTROYED();
// We are placing a block inside an inline. We have to perform a split of this
// inline into continuations. This involves creating an anonymous block box to
Expand All @@ -787,7 +800,7 @@ LayoutBlockFlow* LayoutInline::CreateAnonymousContainerForBlockChildren(
// If inside an inline affected by in-flow positioning the block needs to be
// affected by it too. Giving the block a layer like this allows it to
// collect the x/y offsets from inline parents later.
if (LayoutObject* positioned_ancestor =
if (const LayoutObject* positioned_ancestor =
InFlowPositionedInlineAncestor(this))
new_style->SetPosition(positioned_ancestor->StyleRef().GetPosition());
}
Expand All @@ -800,6 +813,16 @@ LayoutBlockFlow* LayoutInline::CreateAnonymousContainerForBlockChildren(
legacy);
}

LayoutBox* LayoutInline::CreateAnonymousBoxToSplit(
const LayoutBox* box_to_split) const {
NOT_DESTROYED();
DCHECK(box_to_split->IsAnonymous());
DCHECK(IsA<LayoutBlockFlow>(box_to_split));
DCHECK(RuntimeEnabledFeatures::LayoutNGBlockInInlineEnabled());
DCHECK(!ForceLegacyLayout());
return CreateAnonymousContainerForBlockChildren(/* split_flow */ false);
}

void LayoutInline::AddChildToContinuation(LayoutObject* new_child,
LayoutObject* before_child) {
NOT_DESTROYED();
Expand Down
5 changes: 4 additions & 1 deletion third_party/blink/renderer/core/layout/layout_inline.h
Expand Up @@ -369,7 +369,10 @@ class CORE_EXPORT LayoutInline : public LayoutBoxModelObject {
LayoutBoxModelObject* old_cont);

// Create an anonymous block for block children of this inline.
LayoutBlockFlow* CreateAnonymousContainerForBlockChildren(bool split_flow);
LayoutBlockFlow* CreateAnonymousContainerForBlockChildren(
bool split_flow) const;
LayoutBox* CreateAnonymousBoxToSplit(
const LayoutBox* box_to_split) const final;

void UpdateLayout() final {
NOT_DESTROYED();
Expand Down
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<body>
<section>
<div>1</div>
<span>2</span>
<div class="before">3</div>
</section>
<section>
<span>2</span>
<div class="before">3</div>
<div>4</div>
</section>
<section style="columns: 1">
<div>1</div>
<span>2</span>
<div class="before">3</div>
</section>
</body>
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<meta name="assert" content="Test inserting an inline child in the middle of blocks-in-inline">
<link rel="help" href="http://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level" />
<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org" />
<body>
<!-- Insert a span in the middle of blocks-in-inline -->
<section>
<span>
<div>1</div>
<div class="before">3</div>
</span>
</section>
<!-- Insert a span before a block-in-inline -->
<section>
<span>
<div class="before">3</div>
<div>4</div>
</span>
</section>
<!-- Check it works in multicol container -->
<section style="columns: 1">
<span>
<div>1</div>
<div class="before">3</div>
</span>
</section>
<script>
(function () {
document.body.offsetTop;
for (const before of document.getElementsByClassName('before')) {
const span = document.createElement('span');
span.textContent = '2';
before.before(span);
}
})();
</script>
</body>
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<body>
<section>
<div>1</div>
<span>2</span>
<div class="before" style="position: absolute">3</div>
<div>&nbsp;</div>
</section>
<section>
<div>1</div>
<span>2</span>
<div class="before" style="position: absolute">3</div>
<div class="beforeoof">&nbsp;</div>
</section>
</body>
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<meta name="assert" content="Test inserting an inline child before an OOF in blocks-in-inline">
<link rel="help" href="http://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level" />
<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org" />
<body>
<section>
<span>
<div>1</div>
<div class="before" style="position: absolute">3</div>
<div>&nbsp;</div>
</span>
</section>
<!-- The OOF was inserted dynamically -->
<section>
<span>
<div>1</div>
<div class="beforeoof">&nbsp;</div>
</span>
</section>
<script>
(function () {
document.body.offsetTop;
for (const before of document.getElementsByClassName('beforeoof')) {
const oof = document.createElement('div');
oof.classList.add('before')
oof.style.position = 'absolute';
oof.textContent = '3';
before.before(oof);
}

document.body.offsetTop;
for (const before of document.getElementsByClassName('before')) {
const span = document.createElement('span');
span.textContent = '2';
before.before(span);
}
})();
</script>
</body>

0 comments on commit f87440d

Please sign in to comment.