Closed Bug 1405813 Opened 7 years ago Closed 4 years ago

Assertion failure: !childNIF || mFrames.ContainsFrame(childNIF) || (pifEOC && pifEOC->ContainsFrame(childNIF)) || (oc && oc->ContainsFrame(childNIF)) || (eoc && eoc->ContainsFrame(childNIF)) in [@ nsGridContainerFrame::SanityCheckGridItemsBeforeReflow]

Categories

(Core :: Layout, defect)

52 Branch
defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla79
Tracking Status
firefox-esr52 --- wontfix
firefox-esr68 --- wontfix
firefox-esr78 --- wontfix
firefox56 --- wontfix
firefox57 --- wontfix
firefox58 --- wontfix
firefox59 --- wontfix
firefox77 --- wontfix
firefox78 --- wontfix
firefox79 --- fixed

People

(Reporter: tsmith, Assigned: TYLin)

References

(Blocks 1 open bug)

Details

(Keywords: assertion, testcase)

Attachments

(5 files, 1 obsolete file)

Attached file test_case.html
Assertion failure: !childNIF || mFrames.ContainsFrame(childNIF) || (pifEOC && pifEOC->ContainsFrame(childNIF)) || (oc && oc->ContainsFrame(childNIF)) || (eoc && eoc->ContainsFrame(childNIF)), at d/src/layout/generic/nsGridContainerFrame.cpp:6850 #0 nsGridContainerFrame::SanityCheckGridItemsBeforeReflow() const d/src/layout/generic/nsGridContainerFrame.cpp:6847:7 #1 nsGridContainerFrame::Reflow(nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, nsReflowStatus&) d/src/layout/generic/nsGridContainerFrame.cpp:5936:3 #2 nsContainerFrame::ReflowChild(nsIFrame*, nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, mozilla::WritingMode const&, mozilla::LogicalPoint const&, nsSize const&, unsigned int, nsReflowStatus&, nsOverflowContinuationTracker*) d/src/layout/generic/nsContainerFrame.cpp:932:14 #3 nsContainerFrame::ReflowOverflowContainerChildren(nsPresContext*, mozilla::ReflowInput const&, nsOverflowAreas&, unsigned int, nsReflowStatus&, void (*)(nsFrameList&, nsFrameList&, nsContainerFrame*)) d/src/layout/generic/nsContainerFrame.cpp:1179:7 #4 nsBlockFrame::Reflow(nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, nsReflowStatus&) d/src/layout/generic/nsBlockFrame.cpp:1198:5 #5 nsContainerFrame::ReflowChild(nsIFrame*, nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, mozilla::WritingMode const&, mozilla::LogicalPoint const&, nsSize const&, unsigned int, nsReflowStatus&, nsOverflowContinuationTracker*) d/src/layout/generic/nsContainerFrame.cpp:932:14 #6 nsColumnSetFrame::ReflowChildren(mozilla::ReflowOutput&, mozilla::ReflowInput const&, nsReflowStatus&, nsColumnSetFrame::ReflowConfig const&, bool, nsCollapsingMargin*, nsColumnSetFrame::ColumnBalanceData&) d/src/layout/generic/nsColumnSetFrame.cpp:807:7 #7 nsColumnSetFrame::ReflowColumns(mozilla::ReflowOutput&, mozilla::ReflowInput const&, nsReflowStatus&, nsColumnSetFrame::ReflowConfig&, bool, nsCollapsingMargin*, nsColumnSetFrame::ColumnBalanceData&) d/src/layout/generic/nsColumnSetFrame.cpp:514:16 #8 nsColumnSetFrame::Reflow(nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, nsReflowStatus&) d/src/layout/generic/nsColumnSetFrame.cpp:1242:19 #9 nsContainerFrame::ReflowChild(nsIFrame*, nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, mozilla::WritingMode const&, mozilla::LogicalPoint const&, nsSize const&, unsigned int, nsReflowStatus&, nsOverflowContinuationTracker*) d/src/layout/generic/nsContainerFrame.cpp:932:14 #10 nsGridContainerFrame::ReflowInFlowChild(nsIFrame*, nsGridContainerFrame::GridItemInfo const*, nsSize, mozilla::Maybe<int> const&, nsGridContainerFrame::Fragmentainer const*, nsGridContainerFrame::GridReflowInput const&, mozilla::LogicalRect const&, mozilla::ReflowOutput&, nsReflowStatus&) d/src/layout/generic/nsGridContainerFrame.cpp:5125:3 #11 nsGridContainerFrame::ReflowChildren(nsGridContainerFrame::GridReflowInput&, mozilla::LogicalRect const&, mozilla::ReflowOutput&, nsReflowStatus&) d/src/layout/generic/nsGridContainerFrame.cpp:5697:7 #12 nsGridContainerFrame::Reflow(nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, nsReflowStatus&) d/src/layout/generic/nsGridContainerFrame.cpp:6000:11 #13 nsContainerFrame::ReflowChild(nsIFrame*, nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, mozilla::WritingMode const&, mozilla::LogicalPoint const&, nsSize const&, unsigned int, nsReflowStatus&, nsOverflowContinuationTracker*) d/src/layout/generic/nsContainerFrame.cpp:932:14 #14 nsGridContainerFrame::ReflowInFlowChild(nsIFrame*, nsGridContainerFrame::GridItemInfo const*, nsSize, mozilla::Maybe<int> const&, nsGridContainerFrame::Fragmentainer const*, nsGridContainerFrame::GridReflowInput const&, mozilla::LogicalRect const&, mozilla::ReflowOutput&, nsReflowStatus&) d/src/layout/generic/nsGridContainerFrame.cpp:5125:3 #15 nsGridContainerFrame::ReflowChildren(nsGridContainerFrame::GridReflowInput&, mozilla::LogicalRect const&, mozilla::ReflowOutput&, nsReflowStatus&) d/src/layout/generic/nsGridContainerFrame.cpp:5697:7 #16 nsGridContainerFrame::Reflow(nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, nsReflowStatus&) d/src/layout/generic/nsGridContainerFrame.cpp:6000:11 #17 nsContainerFrame::ReflowChild(nsIFrame*, nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, mozilla::WritingMode const&, mozilla::LogicalPoint const&, nsSize const&, unsigned int, nsReflowStatus&, nsOverflowContinuationTracker*) d/src/layout/generic/nsContainerFrame.cpp:932:14 #18 nsCanvasFrame::Reflow(nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, nsReflowStatus&) d/src/layout/generic/nsCanvasFrame.cpp:752:5 #19 nsContainerFrame::ReflowChild(nsIFrame*, nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, mozilla::WritingMode const&, mozilla::LogicalPoint const&, nsSize const&, unsigned int, nsReflowStatus&, nsOverflowContinuationTracker*) d/src/layout/generic/nsContainerFrame.cpp:932:14 #20 nsHTMLScrollFrame::ReflowScrolledFrame(mozilla::ScrollReflowInput*, bool, bool, mozilla::ReflowOutput*, bool) d/src/layout/generic/nsGfxScrollFrame.cpp:550:3 #21 nsHTMLScrollFrame::ReflowContents(mozilla::ScrollReflowInput*, mozilla::ReflowOutput const&) d/src/layout/generic/nsGfxScrollFrame.cpp:662:3 #22 nsHTMLScrollFrame::Reflow(nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, nsReflowStatus&) d/src/layout/generic/nsGfxScrollFrame.cpp:1039:3 #23 nsContainerFrame::ReflowChild(nsIFrame*, nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, int, int, unsigned int, nsReflowStatus&, nsOverflowContinuationTracker*) d/src/layout/generic/nsContainerFrame.cpp:976:14 #24 mozilla::ViewportFrame::Reflow(nsPresContext*, mozilla::ReflowOutput&, mozilla::ReflowInput const&, nsReflowStatus&) d/src/layout/generic/ViewportFrame.cpp:330:7 #25 mozilla::PresShell::DoReflow(nsIFrame*, bool) d/src/layout/base/PresShell.cpp:8939:11 #26 mozilla::PresShell::ProcessReflowCommands(bool) d/src/layout/base/PresShell.cpp:9112:24 #27 mozilla::PresShell::DoFlushPendingNotifications(mozilla::ChangesToFlush) d/src/layout/base/PresShell.cpp:4182:11 #28 nsRefreshDriver::Tick(long, mozilla::TimeStamp) d/src/layout/base/nsRefreshDriver.cpp:1956:16 #29 mozilla::RefreshDriverTimer::TickRefreshDrivers(long, mozilla::TimeStamp, nsTArray<RefPtr<nsRefreshDriver> >&) d/src/layout/base/nsRefreshDriver.cpp:307:7 #30 mozilla::RefreshDriverTimer::Tick(long, mozilla::TimeStamp) d/src/layout/base/nsRefreshDriver.cpp:328:5 #31 mozilla::VsyncRefreshDriverTimer::RunRefreshDrivers(mozilla::TimeStamp) d/src/layout/base/nsRefreshDriver.cpp:770:5 #32 mozilla::VsyncRefreshDriverTimer::RefreshDriverVsyncObserver::TickRefreshDriver(mozilla::TimeStamp) d/src/layout/base/nsRefreshDriver.cpp:683:35 #33 mozilla::VsyncRefreshDriverTimer::RefreshDriverVsyncObserver::ParentProcessVsyncNotifier::Run() d/src/layout/base/nsRefreshDriver.cpp:529:20 #34 nsThread::ProcessNextEvent(bool, bool*) d/src/xpcom/threads/nsThread.cpp:1039:14 #35 NS_ProcessNextEvent(nsIThread*, bool) d/src/xpcom/threads/nsThreadUtils.cpp:524:10 #36 mozilla::ipc::MessagePump::Run(base::MessagePump::Delegate*) d/src/ipc/glue/MessagePump.cpp:97:21 #37 MessageLoop::RunInternal() d/src/ipc/chromium/src/base/message_loop.cc:326:10 #38 MessageLoop::Run() d/src/ipc/chromium/src/base/message_loop.cc:299:3 #39 nsBaseAppShell::Run() d/src/widget/nsBaseAppShell.cpp:158:27 #40 nsAppStartup::Run() d/src/toolkit/components/startup/nsAppStartup.cpp:288:30 #41 XREMain::XRE_mainRun() d/src/toolkit/xre/nsAppRunner.cpp:4701:22 #42 XREMain::XRE_main(int, char**, mozilla::BootstrapConfig const&) d/src/toolkit/xre/nsAppRunner.cpp:4865:8 #43 XRE_main(int, char**, mozilla::BootstrapConfig const&) d/src/toolkit/xre/nsAppRunner.cpp:4960:21 #44 do_main(int, char**, char**) d/src/browser/app/nsBrowserApp.cpp:231:22 #45 main d/src/browser/app/nsBrowserApp.cpp:304:16 #46 __libc_start_main /build/glibc-bfm8X4/glibc-2.23/csu/../csu/libc-start.c:291 #47 _start (firefox+0x41eaf4)
Flags: in-testsuite?
This reproduces as far as a year ago (which is as far back as mozregression goes).
Has Regression Range: --- → no
Attached file columnset-log.txt

If we dump "ColumnSet" log via MOZ_LOG=ColumnSet:5 ./mach run, the assertion
happens in the fourth grid container in the second iteration of column balancing.

GridContainer(summary):frag3
  content:frag1
    Block
    OverflowList<
      video
    >
  OverflowContainerList >
    content:frag2
      <empty>
  >  

GridContainer(summary):frag4 <-- Assertion happens in SanityCheckChildListsBeforeReflow at the beginning of reflow.
  <empty>

The Content doesn't fit since it has overflow list. However the Content has a
next-in-flow in grid frag3's OC list (created in the first iteration of column
balancing). This looks wrong. If it is incomplete, it's next-in-flow should be
in OverflowList; if it is overflow-incomplete, its next-in-flow should be in
ExcessOverflowContainerList. In both cases, it's next-in-flow is expected to be
drained by frag4.

So let's rewind back to take a look at the frame tree at the beginning of
the second column balancing iteration.

===========================================

Frame tree before we call NormalizeChildLists() on frag1. This is the result at
the end of the first iteration of the column balancing.

GridContainer(summary):frag1 <-- At here, before calling NormalizeChildLists()
  Block

GridContainer(summary):frag2
  Link

GridContainer(summary):frag3
  OverflowContainerList<
    Content:frag1
      Block
  >

GridContainer(summary):frag4
  OverflowContainerList<
    Content:frag2
      Video
  >

===========================================

After we call NormalizeChildLists() on grid's frag 1, all the children's
first-in-flows are pulled up to frag1's principal child list, and the Content's
frag2 is put in grid frag2's ExcessOverflowContainerList because it is a
overflow container.

GridContainer(summary):frag1 <-- At here, after calling NormalizeChildLists()
  Block
  Link
  Content:frag1
    Block

GridContainer(summary):frag2
  ExcessOverflowContainerList<
    Content:frag2
      Video
  >

GridContainer(summary):frag3
  <empty>

GridContainer(summary):frag4
  <empty>

===========================================

After reflowing frag1, Link and Content cannot fit, and is on frag1's overflow
list. So, after calling NormalizeChildLists() for frag2, they're pulled from
frag1.

GridContainer(summary):frag1 
  Block

GridContainer(summary):frag2  <-- At here, after calling NormalizeChildLists()
  Link
  Content:frag1
    Block
  ExcessOverflowContainerList< 
    Content:frag2
      Video
  >

GridContainer(summary):frag3
  <empty>

GridContainer(summary):frag4
  <empty>

Note: Content:frag2 is still in ExcessOverflowContainerList after
DrainExcessOverflowContainersList() because we don't drain the child in self
excess overflow containers list if it's prev-in-flow has the same parent. (In
this case both Content:frag1 and Content:frag2 has the same parent.) So
Content:frag2 won't reflow until frag3 pulls it, which is pretty bad ...

(Edit: See mat's response in comment 5, which makes more sense)

===========================================

After my proposed fix, the frame tree after we call NormalizeChildLists() on
grid's frag 1 should look like this. That is, we remove the
NS_FRAME_IS_OVERFLOW_CONTAINER bit for Content:frag2, and put it in grid frag2's
OverflowList. The rationale is that Content:frag2 should start from scratch and
it doesn't necessary a overflow container. If it needs to be a overflow
container after reflow, PushIncompleteChildren() will add the bit, and move it
to a proper child list.

GridContainer(summary):frag1 <-- At here, after calling NormalizeChildLists()
  Block
  Link
  Content:frag1

GridContainer(summary):frag2
  OverflowList< <-- After the fix.
    Content:frag2
  >

GridContainer(summary):frag3
  <empty>

GridContainer(summary):frag4
  <empty>

The rationale is that the pulled up child's first
next-in-flow (childNIF) should start from scratch and it doesn't
necessary be a overflow container. If it needs to be a overflow
container after reflow the child, PushIncompleteChildren() will add the
bit to it, and move it to a proper child list.

Assignee: nobody → aethanyc
Status: NEW → ASSIGNED
Blocks: 1640051

(In reply to Ting-Yu Lin [:TYLin] (UTC-7) from comment #3)

GridContainer(summary):frag1 
  Block

GridContainer(summary):frag2  <-- At here, after calling NormalizeChildLists()
  Link
  Content:frag1
    Block
  ExcessOverflowContainerList< 
    Content:frag2
      Video
  >

GridContainer(summary):frag3
  <empty>

GridContainer(summary):frag4
  <empty>

Note: Content:frag2 is still in ExcessOverflowContainerList after
DrainExcessOverflowContainersList() because we don't drain the child in self
excess overflow containers list if it's prev-in-flow has the same parent. (In
this case both Content:frag1 and Content:frag2 has the same parent.) So
Content:frag2 won't reflow until frag3 pulls it, which is pretty bad ...

Hmm, I disagree with that description. The frame tree above looks entirely valid to me and it's not bad in any way.
Quite the opposite in fact -- reflowing two fragments for the same child in the same container would be bad, that should never happen. So the "Content:frag2 won't reflow until frag3 pulls it" is normal and intentional.

There are two outcomes of reflowing the child frame Content:frag1 above: either it pulls up all children from its next-in-flow because they all fit, i.e. the reflow is COMPLETE, which results in Content:frag2 becoming empty and thus destroyed by DeleteNextInFlowChild (which in turn will make the EOC list empty and also destroyed); or, some child didn't fit in some way, in which case that child is pushed to some overflow list (which one depending on its reflow status).

Attached file frame dump #1

I think the root of the problem is this. We have the frame tree at the top when NormalizeChildLists is called, and the frame tree at the bottom after that call. That later results in the assertion b/c 0x7feff987fde8 (green) being the childNIF isn't in one of the expected child lists; rather it's still in the EOC of the container's prev-in-flow. (It's worth noting that this assertion is fairly harmless - when this container reflows its children the reflow of 0x7feff987f520 (blue) should take care of it (as described above).)

Attached patch hackSplinter Review

So we actually have some code that were intended to handle this case here:
https://searchfox.org/mozilla-central/rev/6dc530332ceb86c9d196c1afe8ba5c90c26be898/layout/generic/nsContainerFrame.cpp#1801-1822
It only checks the container's own OverflowContainers list though, not its prev-in-flow's ExcessOverflowContainers list. (maybe I assumed DrainExcessOverflowContainersList had already been called at this point?)

So if I make it check both (like in this rather hacky patch) then the assertion disappears... (alternatively, just calling DrainExcessOverflowContainersList upfront in NormalizeChildLists should work too).
I'm not saying this is a better fix or anything, but I would encourage you to take another look.

(To be clear, I haven't really looked at what your suggested fix is doing.)

Re comment 5:

reflowing two fragments for the same child in the same container would be bad, that should never happen.

Yeah, I see this is bad after look again at this problem. I've edited my comment 3 to strike the paragraphs of my wrongly proposed fix.

Re comment 7:

So we actually have some code that were intended to handle this case here:
https://searchfox.org/mozilla-central/rev/6dc530332ceb86c9d196c1afe8ba5c90c26be898/layout/generic/nsContainerFrame.cpp#1801-1822
It only checks the container's own OverflowContainers list though, not its prev-in-flow's ExcessOverflowContainers list. (maybe I assumed DrainExcessOverflowContainersList had already been called at this point?)

Nope, we didn't called DrainExcessOverflowContainersList at this point, but I think we should.

Attachment #9154717 - Attachment is obsolete: true
Pushed by aethanyc@gmail.com: https://hg.mozilla.org/integration/autoland/rev/82d3d4f21315 Move prev-in-flow's EOC list to this container frame's OC list in NormalizeChildLists(). r=dholbert
Status: ASSIGNED → RESOLVED
Closed: 4 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla79
Flags: in-testsuite? → in-testsuite+
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: