Bug 1209952 Comment 13 Edit History

Note: The actual edited comment in the bug view page will always show the original commenter’s name and original timestamp.

Ok, I will explain the oddity in the `nsColumnSetFrame` with a further reduced testcase by dropping the surrounding `div` around `dev.float-L` which causes an assertion failure (see my explanation in a comment on phabricator). So the testcase would be:

    <div class="multicol-b">
      <div class="step"></div>
      <div class="float-L"></div>
    </div>


* 1px=60 logical unit. Both `.step` and `.float-L` have 1px height, thus it's totally 2px=120. And we need at least 40 BSize to place it in 3 columns (120 = 40 * 3).

* At the beginning of the log, it's the outer multicol `.multicol-a` relowing the child #0 with `availBSize=1699`, `float-R` consumes 26px=1560, while the inner multicol has 1px border, which consumes 2*1px=120. So the `availableContentBSize` for the inner one only remains `1699 - 1560 - 120 = 19`. But because of the lack of a `std::min` in initialization, it tries to reflow `40`.

* see the inline comments in the log


    // outer reflows child #0
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowing child #0 0x7fe0ef551be8: availBSize=1699
    [00000003] nsBlockFrame:0x7fe0ef551be8 Reflow

    // .float-R reflows
    [00000004] nsBlockFrame:0x7fe0ef551d38 Reflow
    [00000004] nsBlockFrame:0x7fe0ef551d38 Reflow done

    // inner reflows
    [Child 8618: Main Thread]: D/ColumnSet ChooseColumnStrategy: numColumns=3, colISize=4000, expectedISizeLeftOver=0, colBSize=40, colGap=0

    // at this point, we only have 19 available, why try `mColMaxBSize=40`? To exceed 19 is pointless
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Doing column reflow pass: mLastBalanceBSize=40, mColMaxBSize=40, RTL=0, mBalanceColCount=3, mColISize=4000, mColGap=0


    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowing child #0 0x7fe0ef551df0: availBSize=40
    [00000004] nsBlockFrame:0x7fe0ef551df0 Reflow
    [00000005] nsBlockFrame:0x7fe0ef551f40 Reflow
    [00000005] nsBlockFrame:0x7fe0ef551f40 Reflow done
    [00000004] nsBlockFrame:0x7fe0ef551df0 Reflow done
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowed child #0 0x7fe0ef551df0: status=[Complete=N,NIF=Y,Truncated=N,Break=N,FirstLetter=N], desiredSize=(4000,40), CarriedOutBEndMargin=0 (ignored)
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Next childOrigin.iCoord=4060
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowing child #1 0x7fe0ef552398: availBSize=40
    [00000004] nsBlockFrame:0x7fe0ef552398 Reflow
    [00000005] nsBlockFrame:0x7fe0ef552450 Reflow
    [00000005] nsBlockFrame:0x7fe0ef552450 Reflow done
    [00000005] nsBlockFrame:0x7fe0ef551ff8 Reflow
    [00000005] nsBlockFrame:0x7fe0ef551ff8 Reflow done
    [00000004] nsBlockFrame 0x7fe0ef552398 SplitFloat 0x7fe0ef551ff8 -> 0x7fe0ef5522d0
    [00000004] nsBlockFrame:0x7fe0ef552398 Reflow done
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowed child #1 0x7fe0ef552398: status=[Complete=O,NIF=Y,Truncated=N,Break=N,FirstLetter=N], desiredSize=(4000,40), CarriedOutBEndMargin=0 (ignored)
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Next childOrigin.iCoord=8060


    // Why inconsistent height? Both child #0 and #1 have 40, #2 only has 19?
    // That's because https://github.com/mozilla/gecko-dev/blob/master/layout/generic/nsColumnSetFrame.cpp#L712
    // the last column is unbounded, thus it takes all avail size, which is 19, which is **smaller** than the previous columns.
    // Now we take a look at float splitting state. 60+60 should be split into 3 column, thus (40)+(20+20)+(40),
    // So there is already a split for 60 -> 20 + 40.
    // An unfortunate consequence of the oddity here is that the 40 is further split into 40 -> 19 + 21, then
    // 21 is pushed to child#2 as a pushed-float, see splitting `->` below.
    // After this reflow, we end up with split float `1ff8`, `22d0`, `2790`
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowing child #2 0x7fe0ef552558: availBSize=19
    [00000004] nsBlockFrame:0x7fe0ef552558 Reflow
    [00000005] nsBlockFrame:0x7fe0ef5522d0 Reflow
    [00000005] nsBlockFrame:0x7fe0ef5522d0 Reflow done
->  [00000004] nsBlockFrame 0x7fe0ef552558 SplitFloat 0x7fe0ef5522d0 -> 0x7fe0ef552790
    [00000004] nsBlockFrame:0x7fe0ef552558 Reflow done
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowed child #2 0x7fe0ef552558: status=[Complete=O,NIF=Y,Truncated=N,Break=N,FirstLetter=N], desiredSize=(4000,19), CarriedOutBEndMargin=0 (ignored)
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Done column reflow pass: Infeasible :(


    // Now it ends with an infeasible state, we arrive at https://github.com/mozilla/gecko-dev/blob/master/layout/generic/nsColumnSetFrame.cpp#L1111
    // Then we reflow at 19 since it's the available size. But we shouldn't have tried 40 in the first place
    [Child 8618: Main Thread]: D/ColumnSet FindBestBalanceBSize: KnownInfeasibleBSize=40, KnownFeasibleBSize=40
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Doing column reflow pass: mLastBalanceBSize=40, mColMaxBSize=19, RTL=0, mBalanceColCount=3, mColISize=4000, mColGap=0
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowing child #0 0x7fe0ef551df0: availBSize=19
    [00000004] nsBlockFrame:0x7fe0ef551df0 Reflow
    [00000005] nsBlockFrame:0x7fe0ef551f40 Reflow
    [00000005] nsBlockFrame:0x7fe0ef551f40 Reflow done
    [00000004] nsBlockFrame:0x7fe0ef551df0 Reflow done
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowed child #0 0x7fe0ef551df0: status=[Complete=N,NIF=Y,Truncated=N,Break=N,FirstLetter=N], desiredSize=(4000,19), CarriedOutBEndMargin=0 (ignored)
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Next childOrigin.iCoord=4060
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowing child #1 0x7fe0ef552398: availBSize=19
    [00000004] nsBlockFrame:0x7fe0ef552398 Reflow
    [00000005] nsBlockFrame:0x7fe0ef552450 Reflow
    [00000005] nsBlockFrame:0x7fe0ef552450 Reflow done
    [00000004] nsBlockFrame:0x7fe0ef552398 Reflow done
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowed child #1 0x7fe0ef552398: status=[Complete=N,NIF=Y,Truncated=N,Break=N,FirstLetter=N], desiredSize=(4000,19), CarriedOutBEndMargin=0 (ignored)
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Next childOrigin.iCoord=8060


    // first 2 children only consume 19+19=38 height, thus the first split float `1ff8` is entirely overflow in child#1,
    // child#2 drains it, but one of the split float (`2790`) in its next-in-flow chain is still in child#2's pushed-float list.
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowing child #2 0x7fe0ef552558: availBSize=19
    [00000004] nsBlockFrame:0x7fe0ef552558 Reflow
    [00000004] nsBlockFrame:0x7fe0ef552558 DrainOverflowLines 0x7fe0ef551ff8  <- this one, overflow from child #1
    [00000004] nsBlockFrame:0x7fe0ef552558 DrainOverflowLines: nif=0x7fe0ef5522d0
    [00000004] nsBlockFrame:0x7fe0ef552558 DrainOverflowLines: nif=0x7fe0ef552790  <- this one, the third split float still in pushed float list of child#2, which violates assertion.
    Assertion failure: mFloats.ContainsFrame(nif), at /home/lily/supplement/firefox-my-dev/layout/generic/nsBlockFrame.cpp:4756


* Conclusion: we should not try a size that is greater than `availableContentBSize` in the first place, it's a wasted computation at best. It will also make some previously overflow-but-not-drained frames hanging around to do bad things.
Ok, I will explain the oddity in the `nsColumnSetFrame` with a further reduced testcase by dropping the surrounding `div` around `dev.float-L` which causes an assertion failure (see my explanation in a comment on phabricator). So the testcase would be:

    <div class="multicol-b">
      <div class="step"></div>
      <div class="float-L"></div>
    </div>


* 1px=60 logical unit. Both `.step` and `.float-L` have 1px height, thus it's totally 2px=120. And we need at least 40 BSize to place it in 3 columns (120 = 40 * 3).

* At the beginning of the log, it's the outer multicol `.multicol-a` relowing the child #0 with `availBSize=1699`, `float-R` consumes 26px=1560, while the inner multicol has 1px border, which consumes 2*1px=120. So the `availableContentBSize` for the inner one only remains `1699 - 1560 - 120 = 19`. But because of the lack of a `std::min` in initialization, it tries to reflow `40`.

* see the inline comments in the log


    // outer reflows child #0
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowing child #0 0x7fe0ef551be8: availBSize=1699
    [00000003] nsBlockFrame:0x7fe0ef551be8 Reflow

    // .float-R reflows
    [00000004] nsBlockFrame:0x7fe0ef551d38 Reflow
    [00000004] nsBlockFrame:0x7fe0ef551d38 Reflow done

    // inner reflows
    [Child 8618: Main Thread]: D/ColumnSet ChooseColumnStrategy: numColumns=3, colISize=4000, expectedISizeLeftOver=0, colBSize=40, colGap=0

    // at this point, we only have 19 available, why try `mColMaxBSize=40`? To exceed 19 is pointless
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Doing column reflow pass: mLastBalanceBSize=40, mColMaxBSize=40, RTL=0, mBalanceColCount=3, mColISize=4000, mColGap=0


    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowing child #0 0x7fe0ef551df0: availBSize=40
    [00000004] nsBlockFrame:0x7fe0ef551df0 Reflow
    [00000005] nsBlockFrame:0x7fe0ef551f40 Reflow
    [00000005] nsBlockFrame:0x7fe0ef551f40 Reflow done
    [00000004] nsBlockFrame:0x7fe0ef551df0 Reflow done
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowed child #0 0x7fe0ef551df0: status=[Complete=N,NIF=Y,Truncated=N,Break=N,FirstLetter=N], desiredSize=(4000,40), CarriedOutBEndMargin=0 (ignored)
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Next childOrigin.iCoord=4060
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowing child #1 0x7fe0ef552398: availBSize=40
    [00000004] nsBlockFrame:0x7fe0ef552398 Reflow
    [00000005] nsBlockFrame:0x7fe0ef552450 Reflow
    [00000005] nsBlockFrame:0x7fe0ef552450 Reflow done
    [00000005] nsBlockFrame:0x7fe0ef551ff8 Reflow
    [00000005] nsBlockFrame:0x7fe0ef551ff8 Reflow done
    [00000004] nsBlockFrame 0x7fe0ef552398 SplitFloat 0x7fe0ef551ff8 -> 0x7fe0ef5522d0
    [00000004] nsBlockFrame:0x7fe0ef552398 Reflow done
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowed child #1 0x7fe0ef552398: status=[Complete=O,NIF=Y,Truncated=N,Break=N,FirstLetter=N], desiredSize=(4000,40), CarriedOutBEndMargin=0 (ignored)
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Next childOrigin.iCoord=8060


    // Why inconsistent height? Both child #0 and #1 have 40, #2 only has 19?
    // That's because https://github.com/mozilla/gecko-dev/blob/master/layout/generic/nsColumnSetFrame.cpp#L712
    // the last column is unbounded, thus it takes all avail size, which is 19, which is **smaller** than the previous columns.
    // Now we take a look at float splitting state. 60+60 should be split into 3 column, thus (40)+(20+20)+(40),
    // So there is already a split for 60 -> 20 + 40.
    // An unfortunate consequence of the oddity here is that the 40 is further split into 40 -> 19 + 21, then
    // 21 is pushed to child#2 as a pushed-float, see splitting `->` below.
    // After this reflow, we end up with split float `1ff8`, `22d0`, `2790`
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowing child #2 0x7fe0ef552558: availBSize=19
    [00000004] nsBlockFrame:0x7fe0ef552558 Reflow
    [00000005] nsBlockFrame:0x7fe0ef5522d0 Reflow
    [00000005] nsBlockFrame:0x7fe0ef5522d0 Reflow done
->  [00000004] nsBlockFrame 0x7fe0ef552558 SplitFloat 0x7fe0ef5522d0 -> 0x7fe0ef552790
    [00000004] nsBlockFrame:0x7fe0ef552558 Reflow done
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowed child #2 0x7fe0ef552558: status=[Complete=O,NIF=Y,Truncated=N,Break=N,FirstLetter=N], desiredSize=(4000,19), CarriedOutBEndMargin=0 (ignored)
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Done column reflow pass: Infeasible :(


    // Now it ends with an infeasible state, we arrive at https://github.com/mozilla/gecko-dev/blob/master/layout/generic/nsColumnSetFrame.cpp#L1111
    // Then we reflow at 19 since it's the available size. But we shouldn't have tried 40 in the first place
    [Child 8618: Main Thread]: D/ColumnSet FindBestBalanceBSize: KnownInfeasibleBSize=40, KnownFeasibleBSize=40
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Doing column reflow pass: mLastBalanceBSize=40, mColMaxBSize=19, RTL=0, mBalanceColCount=3, mColISize=4000, mColGap=0
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowing child #0 0x7fe0ef551df0: availBSize=19
    [00000004] nsBlockFrame:0x7fe0ef551df0 Reflow
    [00000005] nsBlockFrame:0x7fe0ef551f40 Reflow
    [00000005] nsBlockFrame:0x7fe0ef551f40 Reflow done
    [00000004] nsBlockFrame:0x7fe0ef551df0 Reflow done
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowed child #0 0x7fe0ef551df0: status=[Complete=N,NIF=Y,Truncated=N,Break=N,FirstLetter=N], desiredSize=(4000,19), CarriedOutBEndMargin=0 (ignored)
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Next childOrigin.iCoord=4060
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowing child #1 0x7fe0ef552398: availBSize=19
    [00000004] nsBlockFrame:0x7fe0ef552398 Reflow
    [00000005] nsBlockFrame:0x7fe0ef552450 Reflow
    [00000005] nsBlockFrame:0x7fe0ef552450 Reflow done
    [00000004] nsBlockFrame:0x7fe0ef552398 Reflow done
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowed child #1 0x7fe0ef552398: status=[Complete=N,NIF=Y,Truncated=N,Break=N,FirstLetter=N], desiredSize=(4000,19), CarriedOutBEndMargin=0 (ignored)
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Next childOrigin.iCoord=8060


    // first 2 children only consume 19+19=38 height, thus the first split float `1ff8` is entirely overflow in child#1,
    // child#2 drains it, but one of the split float (`2790`) in its next-in-flow chain is still in child#2's pushed-float list.
    [Child 8618: Main Thread]: D/ColumnSet ReflowChildren: Reflowing child #2 0x7fe0ef552558: availBSize=19
    [00000004] nsBlockFrame:0x7fe0ef552558 Reflow
    [00000004] nsBlockFrame:0x7fe0ef552558 DrainOverflowLines 0x7fe0ef551ff8  <- this one, overflow from child #1
    [00000004] nsBlockFrame:0x7fe0ef552558 DrainOverflowLines: nif=0x7fe0ef5522d0
    [00000004] nsBlockFrame:0x7fe0ef552558 DrainOverflowLines: nif=0x7fe0ef552790  <- this one, the third split float still in pushed float list of child#2, which violates assertion.
    Assertion failure: mFloats.ContainsFrame(nif), at layout/generic/nsBlockFrame.cpp:4756


* Conclusion: we should not try a size that is greater than `availableContentBSize` in the first place, it's a wasted computation at best. It will also make some previously overflow-but-not-drained frames hanging around to do bad things.

Back to Bug 1209952 Comment 13