--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -601,35 +601,63 @@ nsBlockFrame::GetChildLists(nsTArray<Chi
/* virtual */ bool
nsBlockFrame::IsFloatContainingBlock() const
{
return true;
}
static void
-ReparentFrame(nsIFrame* aFrame, nsContainerFrame* aOldParent,
- nsContainerFrame* aNewParent)
+ReparentFrameInternal(nsIFrame* aFrame, nsContainerFrame* aOldParent,
+ nsContainerFrame* aNewParent, bool aMarkDirty)
{
NS_ASSERTION(aOldParent == aFrame->GetParent(),
"Parent not consistent with expectations");
aFrame->SetParent(aNewParent);
+ if (aMarkDirty) {
+ aFrame->AddStateBits(NS_FRAME_IS_DIRTY);
+ }
// When pushing and pulling frames we need to check for whether any
// views need to be reparented
nsContainerFrame::ReparentFrameView(aFrame, aOldParent, aNewParent);
}
+static bool
+ShouldMarkReparentedFramesDirty(nsIFrame* aNewParent,
+ ReparentingDirection aDirection)
+{
+ // Frames going forward must have already been reflowed, or at least marked
+ // dirty. Otherwise frames going backwards (or direction is unknown) may not
+ // be marked dirty yet.
+ return (aDirection != ReparentingDirection::Forwards) &&
+ (aNewParent->GetStateBits() & NS_FRAME_IS_DIRTY);
+}
+
+// Because a frame with NS_FRAME_IS_DIRTY marks all of its children dirty at
+// the start of its reflow, when we move a frame from a later frame backwards to
+// an earlier frame, and the earlier frame has NS_FRAME_IS_DIRTY (though that
+// should corresponded with the later frame having NS_FRAME_IS_DIRTY), we need
+// to add NS_FRAME_IS_DIRTY to the reparented frame.
+static void
+ReparentFrame(nsIFrame* aFrame, nsContainerFrame* aOldParent,
+ nsContainerFrame* aNewParent, ReparentingDirection aDirection)
+{
+ const bool markDirty = ShouldMarkReparentedFramesDirty(aNewParent, aDirection);
+ ReparentFrameInternal(aFrame, aOldParent, aNewParent, markDirty);
+}
+
static void
ReparentFrames(nsFrameList& aFrameList, nsContainerFrame* aOldParent,
- nsContainerFrame* aNewParent)
-{
+ nsContainerFrame* aNewParent, ReparentingDirection aDirection)
+{
+ const bool markDirty = ShouldMarkReparentedFramesDirty(aNewParent, aDirection);
for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
- ReparentFrame(e.get(), aOldParent, aNewParent);
+ ReparentFrameInternal(e.get(), aOldParent, aNewParent, markDirty);
}
}
/**
* Remove the first line from aFromLines and adjust the associated frame list
* aFromFrames accordingly. The removed line is assigned to *aOutLine and
* a frame list with its frames is assigned to *aOutFrames, i.e. the frames
* that were extracted from the head of aFromFrames.
@@ -2125,17 +2153,18 @@ void
nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame, nsBlockFrame* aOldParent,
bool aReparentSiblings) {
nsFrameList list;
aOldParent->CollectFloats(aFirstFrame, list, aReparentSiblings);
if (list.NotEmpty()) {
for (nsIFrame* f : list) {
MOZ_ASSERT(!(f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT),
"CollectFloats should've removed that bit");
- ReparentFrame(f, aOldParent, this);
+ // XXX: "Backwards" to force NS_FRAME_IS_DIRTY for now, see next patch.
+ ReparentFrame(f, aOldParent, this, ReparentingDirection::Backwards);
}
mFloats.AppendFrames(nullptr, list);
}
}
static void DumpLine(const BlockReflowInput& aState, nsLineBox* aLine,
nscoord aDeltaBCoord, int32_t aDeltaIndent) {
#ifdef DEBUG
@@ -2622,17 +2651,18 @@ nsBlockFrame::ReflowDirtyLines(BlockRefl
!pulledLine->mFirstChild, "bad empty line");
nextInFlow->FreeLineBox(pulledLine);
continue;
}
if (pulledLine == nextInFlow->GetLineCursor()) {
nextInFlow->ClearLineCursor();
}
- ReparentFrames(pulledFrames, nextInFlow, this);
+ ReparentFrames(pulledFrames, nextInFlow, this,
+ ReparentingDirection::Backwards);
NS_ASSERTION(pulledFrames.LastChild() == pulledLine->LastChild(),
"Unexpected last frame");
NS_ASSERTION(aState.mPrevChild || mLines.empty(), "should have a prevchild here");
NS_ASSERTION(aState.mPrevChild == mFrames.LastChild(),
"Incorrect aState.mPrevChild before inserting line at end");
// Shift pulledLine's frames into our mFrames list.
@@ -2917,17 +2947,17 @@ nsBlockFrame::PullFrameFrom(nsLineBox*
// need to add it to our sibling list.
MOZ_ASSERT(aLine == mLines.back());
MOZ_ASSERT(aFromLine == aFromContainer->mLines.begin(),
"should only pull from first line");
aFromContainer->mFrames.RemoveFrame(frame);
// When pushing and pulling frames we need to check for whether any
// views need to be reparented.
- ReparentFrame(frame, aFromContainer, this);
+ ReparentFrame(frame, aFromContainer, this, ReparentingDirection::Backwards);
mFrames.AppendFrame(nullptr, frame);
// The frame might have (or contain) floats that need to be brought
// over too. (pass 'false' since there are no siblings to check)
ReparentFloats(frame, aFromContainer, false);
} else {
MOZ_ASSERT(aLine == aFromLine.prev());
}
@@ -3666,18 +3696,20 @@ nsBlockFrame::ReflowBlockFrame(BlockRefl
if (!madeContinuation &&
(NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) {
nsOverflowContinuationTracker::AutoFinish fini(aState.mOverflowTracker, frame);
nsContainerFrame* parent = nextFrame->GetParent();
nsresult rv = parent->StealFrame(nextFrame);
if (NS_FAILED(rv)) {
return;
}
- if (parent != this)
- ReparentFrame(nextFrame, parent, this);
+ if (parent != this) {
+ ReparentFrame(nextFrame, parent, this,
+ ReparentingDirection::Variable);
+ }
mFrames.InsertFrame(nullptr, frame, nextFrame);
madeContinuation = true; // needs to be added to mLines
nextFrame->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
frameReflowStatus.SetNextInFlowNeedsReflow();
}
// Push continuation to a new line, but only if we actually made one.
if (madeContinuation) {
@@ -4326,17 +4358,17 @@ nsBlockFrame::SplitFloat(BlockReflowInpu
MOZ_ASSERT(aState.mBlock == this);
nsIFrame* nextInFlow = aFloat->GetNextInFlow();
if (nextInFlow) {
nsContainerFrame *oldParent = nextInFlow->GetParent();
DebugOnly<nsresult> rv = oldParent->StealFrame(nextInFlow);
NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failed");
if (oldParent != this) {
- ReparentFrame(nextInFlow, oldParent, this);
+ ReparentFrame(nextInFlow, oldParent, this, ReparentingDirection::Backwards);
}
if (!aFloatStatus.IsOverflowIncomplete()) {
nextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
}
} else {
nextInFlow = aState.mPresContext->PresShell()->FrameConstructor()->
CreateContinuingFrame(aState.mPresContext, aFloat, this);
}
@@ -4845,31 +4877,33 @@ nsBlockFrame::DrainOverflowLines()
// Steal the prev-in-flow's overflow lines and prepend them.
bool didFindOverflow = false;
nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
if (prevBlock) {
prevBlock->ClearLineCursor();
FrameLines* overflowLines = prevBlock->RemoveOverflowLines();
if (overflowLines) {
// Make all the frames on the overflow line list mine.
- ReparentFrames(overflowLines->mFrames, prevBlock, this);
+ ReparentFrames(overflowLines->mFrames, prevBlock, this,
+ ReparentingDirection::Forwards);
// Make the overflow out-of-flow frames mine too.
nsAutoOOFFrameList oofs(prevBlock);
if (oofs.mList.NotEmpty()) {
// In case we own a next-in-flow of any of the drained frames, then
// those are now not PUSHED_FLOATs anymore.
for (nsFrameList::Enumerator e(oofs.mList); !e.AtEnd(); e.Next()) {
nsIFrame* nif = e.get()->GetNextInFlow();
for (; nif && nif->GetParent() == this; nif = nif->GetNextInFlow()) {
MOZ_ASSERT(mFloats.ContainsFrame(nif));
nif->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT);
}
}
- ReparentFrames(oofs.mList, prevBlock, this);
+ ReparentFrames(oofs.mList, prevBlock, this,
+ ReparentingDirection::Forwards);
mFloats.InsertFrames(nullptr, nullptr, oofs.mList);
}
if (!mLines.empty()) {
// Remember to recompute the margins on the first line. This will
// also recompute the correct deltaBCoord if necessary.
mLines.front()->MarkPreviousMarginDirty();
}