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

Set up inline continuations correctly when not inserting a new child.

LayoutInline::ChildBecameNonInline() is called when a child of an inline
becomes in-flow block-level (e.g. when its position changes from
absolute to static) and we need to set up a continuation chain because
of this. Part of this operation is to wrap the in-flow block-level child
inside a new anonymous containing block.

AddChildIgnoringContinuation(), which is what we call when inserting new
children, did a few nice things to such anonymous containing blocks,
which ChildBecameNonInline() didn't do, most crucially, setting the
position of the anonymous containing block to relative if the inline is
relatively positioned (to contain absolutely positioned descendants). So
if the continuation was set up as a reaction to something becoming
block-level, rather than being set up as part of inserting new children,
the anonymous containing block of block-level children of the inline
didn't get position:relative, which made any absolutely positioned
children escape its relatively positioned inline parent, and find a
containing block further up (the viewport, in the included testcase).

To fix this, break out the code that sets up such an anonymous block
into a separate method, and have ChildBecameNonInline() call it.

Bug: 732397
Change-Id: I06b120c3173403ba6db3b93019e95a66575ba0e1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1833581Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Reviewed-by: default avatarAleks Totic <atotic@chromium.org>
Commit-Queue: Morten Stenshorne <mstensho@chromium.org>
Cr-Commit-Position: refs/heads/master@{#701646}
parent c8b3d68a
...@@ -508,39 +508,7 @@ void LayoutInline::AddChildIgnoringContinuation(LayoutObject* new_child, ...@@ -508,39 +508,7 @@ void LayoutInline::AddChildIgnoringContinuation(LayoutObject* new_child,
if (!new_child->IsInline() && !new_child->IsFloatingOrOutOfFlowPositioned() && if (!new_child->IsInline() && !new_child->IsFloatingOrOutOfFlowPositioned() &&
!new_child->IsTablePart()) { !new_child->IsTablePart()) {
// We are placing a block inside an inline. We have to perform a split of LayoutBlockFlow* new_box = CreateAnonymousContainerForBlockChildren();
// this inline into continuations. This involves creating an anonymous
// block box to hold |newChild|. We then make that block box a continuation
// of this inline. We take all of the children after |beforeChild| and put
// them in a clone of this object.
scoped_refptr<ComputedStyle> new_style =
ComputedStyle::CreateAnonymousStyleWithDisplay(StyleRef(),
EDisplay::kBlock);
const LayoutBlock* containing_block = ContainingBlock();
// The anon block we create here doesn't exist in the CSS spec, so
// we need to ensure that any blocks it contains inherit properly
// from its true parent. This means they must use the direction set by the
// anon block's containing block, so we need to prevent the anon block
// from inheriting direction from the inline. If there are any other
// inheritable properties that apply to block and inline elements
// but only affect the layout of children we will want to special-case
// them here too. Writing-mode would be one if it didn't create a
// formatting context of its own, removing the need for continuations.
new_style->SetDirection(containing_block->StyleRef().Direction());
// 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 =
InFlowPositionedInlineAncestor(this))
new_style->SetPosition(positioned_ancestor->StyleRef().GetPosition());
LegacyLayout legacy = containing_block->ForceLegacyLayout()
? LegacyLayout::kForce
: LegacyLayout::kAuto;
LayoutBlockFlow* new_box = LayoutBlockFlow::CreateAnonymous(
&GetDocument(), std::move(new_style), legacy);
LayoutBoxModelObject* old_continuation = Continuation(); LayoutBoxModelObject* old_continuation = Continuation();
SetContinuation(new_box); SetContinuation(new_box);
...@@ -731,6 +699,41 @@ void LayoutInline::SplitFlow(LayoutObject* before_child, ...@@ -731,6 +699,41 @@ void LayoutInline::SplitFlow(LayoutObject* before_child,
layout_invalidation_reason::kAnonymousBlockChange); layout_invalidation_reason::kAnonymousBlockChange);
} }
LayoutBlockFlow* LayoutInline::CreateAnonymousContainerForBlockChildren() {
// 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
// hold |newChild|. We then make that block box a continuation of this
// inline. We take all of the children after |beforeChild| and put them in a
// clone of this object.
scoped_refptr<ComputedStyle> new_style =
ComputedStyle::CreateAnonymousStyleWithDisplay(StyleRef(),
EDisplay::kBlock);
const LayoutBlock* containing_block = ContainingBlock();
// The anon block we create here doesn't exist in the CSS spec, so we need to
// ensure that any blocks it contains inherit properly from its true
// parent. This means they must use the direction set by the anon block's
// containing block, so we need to prevent the anon block from inheriting
// direction from the inline. If there are any other inheritable properties
// that apply to block and inline elements but only affect the layout of
// children we will want to special-case them here too. Writing-mode would be
// one if it didn't create a formatting context of its own, removing the need
// for continuations.
new_style->SetDirection(containing_block->StyleRef().Direction());
// 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 = InFlowPositionedInlineAncestor(this))
new_style->SetPosition(positioned_ancestor->StyleRef().GetPosition());
LegacyLayout legacy = containing_block->ForceLegacyLayout()
? LegacyLayout::kForce
: LegacyLayout::kAuto;
return LayoutBlockFlow::CreateAnonymous(&GetDocument(), std::move(new_style),
legacy);
}
void LayoutInline::AddChildToContinuation(LayoutObject* new_child, void LayoutInline::AddChildToContinuation(LayoutObject* new_child,
LayoutObject* before_child) { LayoutObject* before_child) {
// A continuation always consists of two potential candidates: an inline or an // A continuation always consists of two potential candidates: an inline or an
...@@ -1474,8 +1477,7 @@ PaintLayerType LayoutInline::LayerTypeRequired() const { ...@@ -1474,8 +1477,7 @@ PaintLayerType LayoutInline::LayerTypeRequired() const {
void LayoutInline::ChildBecameNonInline(LayoutObject* child) { void LayoutInline::ChildBecameNonInline(LayoutObject* child) {
// We have to split the parent flow. // We have to split the parent flow.
auto* new_box = LayoutBlockFlow* new_box = CreateAnonymousContainerForBlockChildren();
To<LayoutBlockFlow>(ContainingBlock()->CreateAnonymousBlock());
LayoutBoxModelObject* old_continuation = Continuation(); LayoutBoxModelObject* old_continuation = Continuation();
SetContinuation(new_box); SetContinuation(new_box);
LayoutObject* before_child = child->NextSibling(); LayoutObject* before_child = child->NextSibling();
......
...@@ -327,6 +327,9 @@ class CORE_EXPORT LayoutInline : public LayoutBoxModelObject { ...@@ -327,6 +327,9 @@ class CORE_EXPORT LayoutInline : public LayoutBoxModelObject {
LayoutObject* new_child, LayoutObject* new_child,
LayoutBoxModelObject* old_cont); LayoutBoxModelObject* old_cont);
// Create an anoymous block for block children of this inline.
LayoutBlockFlow* CreateAnonymousContainerForBlockChildren();
void UpdateLayout() final { NOTREACHED(); } // Do nothing for layout() void UpdateLayout() final { NOTREACHED(); } // Do nothing for layout()
void Paint(const PaintInfo&) const final; void Paint(const PaintInfo&) const final;
......
<!DOCTYPE html>
<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=732397">
<link rel="help" href="https://www.w3.org/TR/CSS22/visudet.html#containing-block-details">
<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div style="width:50px; height:50px; padding-left:50px; padding-top:50px; background:red;">
<span style="position:relative;">
<div id="victim" style="position:absolute; top:-50px; left:-50px; width:100px; height:100px; background:green;"></div>
</span>
</div>
<script>
document.body.offsetTop;
victim.style.position = "static";
document.body.offsetTop;
victim.style.position = "absolute";
</script>
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