Bug 1504053 - Reframe multi-column container if inserting a subtree with a column-span child. r=dbaron

Differential Revision: https://phabricator.services.mozilla.com/D16077

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Ting-Yu Lin 2019-01-31 02:22:49 +00:00
parent 6df4e561ff
commit 5ec0177dc3
7 changed files with 234 additions and 1 deletions

View File

@ -5932,7 +5932,7 @@ void nsCSSFrameConstructor::AppendFramesToParent(
return;
}
// If we're appending a list of frames at the last continuations of a
// If we're appending a list of frames to the last continuations of a
// ::-moz-column-content, we may need to create column-span siblings for them.
if (!nextSibling && IsLastContinuationForColumnContent(aParentFrame)) {
// Extract any initial non-column-span kids, and append them to
@ -6871,6 +6871,14 @@ void nsCSSFrameConstructor::ContentAppended(nsIContent* aFirstNewContent,
AppendFrames(outerTable, nsIFrame::kCaptionList, captionItems);
}
LAYOUT_PHASE_TEMP_EXIT();
if (StaticPrefs::layout_css_column_span_enabled() &&
MaybeRecreateForColumnSpan(state, parentFrame, frameItems, prevSibling)) {
LAYOUT_PHASE_TEMP_REENTER();
return;
}
LAYOUT_PHASE_TEMP_REENTER();
if (frameItems.NotEmpty()) { // append the in-flow kids
AppendFramesToParent(state, parentFrame, frameItems, prevSibling);
}
@ -7326,6 +7334,15 @@ void nsCSSFrameConstructor::ContentRangeInserted(
}
}
LAYOUT_PHASE_TEMP_EXIT();
if (StaticPrefs::layout_css_column_span_enabled() &&
MaybeRecreateForColumnSpan(state, insertion.mParentFrame, frameItems,
prevSibling)) {
LAYOUT_PHASE_TEMP_REENTER();
return;
}
LAYOUT_PHASE_TEMP_REENTER();
if (frameItems.NotEmpty()) {
// Notify the parent frame
if (isAppend) {
@ -10857,6 +10874,61 @@ nsFrameItems nsCSSFrameConstructor::CreateColumnSpanSiblings(
return siblings;
}
bool nsCSSFrameConstructor::MaybeRecreateForColumnSpan(
nsFrameConstructorState& aState, nsContainerFrame* aParentFrame,
nsFrameList& aFrameList, nsIFrame* aPrevSibling) {
MOZ_ASSERT(StaticPrefs::layout_css_column_span_enabled(),
"Call this only when layout.css.column-span.enabled is true!");
if (!aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) {
return false;
}
MOZ_ASSERT(!IsFramePartOfIBSplit(aParentFrame),
"We should have wiped aParentFrame in WipeContainingBlock if it's "
"part of IB split!");
nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
if (!nextSibling && IsLastContinuationForColumnContent(aParentFrame)) {
// We are appending a list of frames to the last continuation of a
// ::-moz-column-content. This is the case where we can fix the frame tree
// instead of reframing the containing block. Return false and let
// AppendFramesToParent() deal with this.
return false;
}
auto HasColumnSpan = [](const nsFrameList& aList) {
for (nsIFrame* f : aList) {
if (f->IsColumnSpan()) {
return true;
}
}
return false;
};
if (HasColumnSpan(aFrameList)) {
// If any frame in the frame list has "column-span:all" style, i.e. a
// -moz-column-span-wrapper frame, we need to reframe the multi-column
// containing block.
//
// We can only be here if none of the new inserted nsIContent* nodes (via
// ContentAppended or ContentRangeInserted) have column-span:all style, yet
// some of them have column-span:all descendants. Sadly, there's no way to
// detect this by checking FrameConstructionItems in WipeContainingBlock().
// Otherwise, we would have already wiped the multi-column containing block.
PROFILER_TRACING("Layout",
"Reframe multi-column after constructing frame list",
LAYOUT, TRACING_EVENT);
aFrameList.DestroyFrames();
RecreateFramesForContent(
GetMultiColumnContainingBlockFor(aParentFrame)->GetContent(),
InsertionKind::Async);
return true;
}
return false;
}
nsIFrame* nsCSSFrameConstructor::ConstructInline(
nsFrameConstructorState& aState, FrameConstructionItem& aItem,
nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,

View File

@ -1891,6 +1891,23 @@ class nsCSSFrameConstructor final : public nsFrameManager {
nsFrameList& aChildList,
nsIFrame* aPositionedFrame);
// Reconstruct the multi-column containing block of aParentFrame when we want
// to insert aFrameList into aParentFrame immediately after aPrevSibling but
// cannot fix the frame tree because aFrameList contains some column-spans.
//
// @param aParentFrame the to-be parent frame for aFrameList.
// @param aFrameList the frames to be inserted. It will be cleared if we need
// reconstruction.
// @param aPrevSibling the position where the frames in aFrameList are going
// to be inserted. Nullptr means aFrameList is being inserted at
// the beginning.
// @return true if the multi-column containing block of aParentFrame is
// reconstructed; false otherwise.
bool MaybeRecreateForColumnSpan(nsFrameConstructorState& aState,
nsContainerFrame* aParentFrame,
nsFrameList& aFrameList,
nsIFrame* aPrevSibling);
nsIFrame* ConstructInline(nsFrameConstructorState& aState,
FrameConstructionItem& aItem,
nsContainerFrame* aParentFrame,

View File

@ -0,0 +1,2 @@
[multicol-span-all-dynamic-add-011.html]
prefs: [layout.css.column-span.enabled:true]

View File

@ -0,0 +1,2 @@
[multicol-span-all-dynamic-add-012.html]
prefs: [layout.css.column-span.enabled:true]

View File

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html class="reftest-wait">
<meta charset="utf-8">
<title>CSS Multi-column Layout Test: Insert a block containing a spanner kid. The spanner kid should correctly span across all columns</title>
<link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
<link rel="help" href="https://drafts.csswg.org/css-multicol-1/#column-span">
<link rel="match" href="multicol-span-all-dynamic-add-003-ref.html">
<meta name="assert" content="This test checks that an inserted block containing 'column-span' element should be rendered correctly.">
<script>
function runTest() {
document.body.offsetHeight;
// Create a subtree like the following, and insert it into column as the
// first child.
// <div>
// block1
// <h3>spanner</h3>
// </div>
var spanner = document.createElement("h3");
var spannerText = document.createTextNode("spanner");
spanner.appendChild(spannerText);
var block1 = document.createElement("div");
var block1Text = document.createTextNode("block1");
block1.appendChild(block1Text)
block1.appendChild(spanner);
var column = document.getElementById("column");
column.insertBefore(block1, column.children[0]);
document.documentElement.removeAttribute("class");
}
</script>
<style>
#column {
column-count: 3;
column-rule: 6px solid;
width: 400px;
outline: 1px solid black;
}
h3 {
column-span: all;
outline: 1px solid blue;
}
</style>
<body onload="runTest();">
<article id="column">
<div>block2</div>
</article>
</body>
</html>

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<title>CSS Multi-column Layout Test Reference: Append a block containing a spanner kid. The spanner kid should correctly span across all columns</title>
<link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
<style>
#column {
column-count: 3;
column-rule: 6px solid;
width: 400px;
outline: 1px solid black;
}
h3 {
column-span: all;
outline: 1px solid blue;
}
</style>
<body>
<article id="column">
<div>block1
<div>
<h3>spanner</h3>
block2
</div>
</div>
</article>
</body>
</html>

View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html class="reftest-wait">
<meta charset="utf-8">
<title>CSS Multi-column Layout Test: Append a block containing a spanner kid. The spanner kid should correctly span across all columns</title>
<link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
<link rel="help" href="https://drafts.csswg.org/css-multicol-1/#column-span">
<link rel="match" href="multicol-span-all-dynamic-add-012-ref.html">
<meta name="assert" content="This test checks that an appended block containing 'column-span' element should be rendered correctly.">
<script>
function runTest() {
document.body.offsetHeight;
// Create a subtree like the following, and append it to block1.
// <div>
// <h3>spanner</h3>
// block2
// </div>
var spanner = document.createElement("h3");
var spannerText = document.createTextNode("spanner");
spanner.appendChild(spannerText);
var block2 = document.createElement("div");
var block2Text = document.createTextNode("block2");
block2.appendChild(spanner);
block2.appendChild(block2Text)
var block1 = document.getElementById("block1");
block1.appendChild(block2);
document.documentElement.removeAttribute("class");
}
</script>
<style>
#column {
column-count: 3;
column-rule: 6px solid;
width: 400px;
outline: 1px solid black;
}
h3 {
column-span: all;
outline: 1px solid blue;
}
</style>
<body onload="runTest();">
<article id="column">
<div id="block1">block1</div>
</article>
</body>
</html>