static
void DebugCheckChildSize(nsIFrame*            aChild,
                         ReflowOutput& aMet)
{
  WritingMode wm = aMet.GetWritingMode();
  if ((aMet.ISize(wm) < 0) || (aMet.ISize(wm) > PROBABLY_TOO_LARGE)) {
    printf("WARNING: cell content %p has large inline size %d \n",
           static_cast<void*>(aChild), int32_t(aMet.ISize(wm)));
  }
}
void
nsSimplePageSequenceFrame::SetDesiredSize(ReflowOutput& aDesiredSize,
                                          const ReflowInput& aReflowInput,
                                          nscoord aWidth,
                                          nscoord aHeight)
{
  // Aim to fill the whole size of the document, not only so we
  // can act as a background in print preview but also handle overflow
  // in child page frames correctly.
  // Use availableISize so we don't cause a needless horizontal scrollbar.
  WritingMode wm = aReflowInput.GetWritingMode();
  nscoord scaledWidth = aWidth * PresContext()->GetPrintPreviewScale();
  nscoord scaledHeight = aHeight * PresContext()->GetPrintPreviewScale();

  nscoord scaledISize = (wm.IsVertical() ? scaledHeight : scaledWidth);
  nscoord scaledBSize = (wm.IsVertical() ? scaledWidth : scaledHeight);

  aDesiredSize.ISize(wm) = std::max(scaledISize, aReflowInput.AvailableISize());
  aDesiredSize.BSize(wm) = std::max(scaledBSize, aReflowInput.ComputedBSize());
}
示例#3
0
/* virtual */ void
nsRubyTextFrame::Reflow(nsPresContext* aPresContext,
                        ReflowOutput& aDesiredSize,
                        const ReflowInput& aReflowInput,
                        nsReflowStatus& aStatus)
{
  // Even if we want to hide this frame, we have to reflow it first.
  // If we leave it dirty, changes to its content will never be
  // propagated to the ancestors, then it won't be displayed even if
  // the content is no longer the same, until next reflow triggered by
  // some other change. In general, we always reflow all the frames we
  // created. There might be other problems if we don't do that.
  nsRubyContentFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);

  if (IsAutoHidden()) {
    // Reset the ISize. The BSize is not changed so that it won't
    // affect vertical positioning in unexpected way.
    WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode();
    aDesiredSize.ISize(lineWM) = 0;
    aDesiredSize.SetOverflowAreasToDesiredBounds();
  }
}
示例#4
0
/* virtual */ void
nsRubyFrame::Reflow(nsPresContext* aPresContext,
                    ReflowOutput& aDesiredSize,
                    const ReflowInput& aReflowInput,
                    nsReflowStatus& aStatus)
{
  MarkInReflow();
  DO_GLOBAL_REFLOW_COUNT("nsRubyFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
  
  if (!aReflowInput.mLineLayout) {
    NS_ASSERTION(aReflowInput.mLineLayout,
                 "No line layout provided to RubyFrame reflow method.");
    aStatus = NS_FRAME_COMPLETE;
    return;
  }

  // Grab overflow frames from prev-in-flow and its own.
  MoveOverflowToChildList();

  // Clear leadings
  mBStartLeading = mBEndLeading = 0;

  // Begin the span for the ruby frame
  WritingMode frameWM = aReflowInput.GetWritingMode();
  WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode();
  LogicalMargin borderPadding = aReflowInput.ComputedLogicalBorderPadding();
  nscoord startEdge = 0;
  const bool boxDecorationBreakClone =
    StyleBorder()->mBoxDecorationBreak == StyleBoxDecorationBreak::Clone;
  if (boxDecorationBreakClone || !GetPrevContinuation()) {
    startEdge = borderPadding.IStart(frameWM);
  }
  NS_ASSERTION(aReflowInput.AvailableISize() != NS_UNCONSTRAINEDSIZE,
               "should no longer use available widths");
  nscoord availableISize = aReflowInput.AvailableISize();
  availableISize -= startEdge + borderPadding.IEnd(frameWM);
  aReflowInput.mLineLayout->BeginSpan(this, &aReflowInput,
                                      startEdge, availableISize, &mBaseline);

  aStatus = NS_FRAME_COMPLETE;
  for (RubySegmentEnumerator e(this); !e.AtEnd(); e.Next()) {
    ReflowSegment(aPresContext, aReflowInput, e.GetBaseContainer(), aStatus);

    if (NS_INLINE_IS_BREAK(aStatus)) {
      // A break occurs when reflowing the segment.
      // Don't continue reflowing more segments.
      break;
    }
  }

  ContinuationTraversingState pullState(this);
  while (aStatus == NS_FRAME_COMPLETE) {
    nsRubyBaseContainerFrame* baseContainer =
      PullOneSegment(aReflowInput.mLineLayout, pullState);
    if (!baseContainer) {
      // No more continuations after, finish now.
      break;
    }
    ReflowSegment(aPresContext, aReflowInput, baseContainer, aStatus);
  }
  // We never handle overflow in ruby.
  MOZ_ASSERT(!NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus));

  aDesiredSize.ISize(lineWM) = aReflowInput.mLineLayout->EndSpan(this);
  if (boxDecorationBreakClone || !GetPrevContinuation()) {
    aDesiredSize.ISize(lineWM) += borderPadding.IStart(frameWM);
  }
  if (boxDecorationBreakClone || NS_FRAME_IS_COMPLETE(aStatus)) {
    aDesiredSize.ISize(lineWM) += borderPadding.IEnd(frameWM);
  }

  nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize,
                                         borderPadding, lineWM, frameWM);
}
示例#5
0
void
nsInlineFrame::ReflowFrames(nsPresContext* aPresContext,
                            const ReflowInput& aReflowInput,
                            InlineReflowInput& irs,
                            ReflowOutput& aMetrics,
                            nsReflowStatus& aStatus)
{
  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");

  nsLineLayout* lineLayout = aReflowInput.mLineLayout;
  bool inFirstLine = aReflowInput.mLineLayout->GetInFirstLine();
  RestyleManager* restyleManager = aPresContext->RestyleManager();
  WritingMode frameWM = aReflowInput.GetWritingMode();
  WritingMode lineWM = aReflowInput.mLineLayout->mRootSpan->mWritingMode;
  LogicalMargin framePadding = aReflowInput.ComputedLogicalBorderPadding();
  nscoord startEdge = 0;
  const bool boxDecorationBreakClone =
    MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
                   StyleBoxDecorationBreak::Clone);
  // Don't offset by our start borderpadding if we have a prev continuation or
  // if we're in a part of an {ib} split other than the first one. For
  // box-decoration-break:clone we always offset our start since all
  // continuations have border/padding.
  if ((!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) ||
      boxDecorationBreakClone) {
    startEdge = framePadding.IStart(frameWM);
  }
  nscoord availableISize = aReflowInput.AvailableISize();
  NS_ASSERTION(availableISize != NS_UNCONSTRAINEDSIZE,
               "should no longer use available widths");
  // Subtract off inline axis border+padding from availableISize
  availableISize -= startEdge;
  availableISize -= framePadding.IEnd(frameWM);
  lineLayout->BeginSpan(this, &aReflowInput, startEdge,
                        startEdge + availableISize, &mBaseline);

  // First reflow our principal children.
  nsIFrame* frame = mFrames.FirstChild();
  bool done = false;
  while (frame) {
    // Check if we should lazily set the child frame's parent pointer.
    if (irs.mSetParentPointer) {
      nsIFrame* child = frame;
      do {
        child->SetParent(this);
        if (inFirstLine) {
          restyleManager->ReparentStyleContext(child);
          nsLayoutUtils::MarkDescendantsDirty(child);
        }
        // We also need to do the same for |frame|'s next-in-flows that are in
        // the sibling list. Otherwise, if we reflow |frame| and it's complete
        // we'll crash when trying to delete its next-in-flow.
        // This scenario doesn't happen often, but it can happen.
        nsIFrame* nextSibling = child->GetNextSibling();
        child = child->GetNextInFlow();
        if (MOZ_UNLIKELY(child)) {
          while (child != nextSibling && nextSibling) {
            nextSibling = nextSibling->GetNextSibling();
          }
          if (!nextSibling) {
            child = nullptr;
          }
        }
        MOZ_ASSERT(!child || mFrames.ContainsFrame(child));
      } while (child);

      // Fix the parent pointer for ::first-letter child frame next-in-flows,
      // so nsFirstLetterFrame::Reflow can destroy them safely (bug 401042).
      nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(frame);
      if (realFrame->IsLetterFrame()) {
        nsIFrame* child = realFrame->PrincipalChildList().FirstChild();
        if (child) {
          NS_ASSERTION(child->IsTextFrame(), "unexpected frame type");
          nsIFrame* nextInFlow = child->GetNextInFlow();
          for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) {
            NS_ASSERTION(nextInFlow->IsTextFrame(), "unexpected frame type");
            if (mFrames.ContainsFrame(nextInFlow)) {
              nextInFlow->SetParent(this);
              if (inFirstLine) {
                restyleManager->ReparentStyleContext(nextInFlow);
                nsLayoutUtils::MarkDescendantsDirty(nextInFlow);
              }
            }
            else {
#ifdef DEBUG
              // Once we find a next-in-flow that isn't ours none of the
              // remaining next-in-flows should be either.
              for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) {
                NS_ASSERTION(!mFrames.ContainsFrame(nextInFlow),
                             "unexpected letter frame flow");
              }
#endif
              break;
            }
          }
        }
      }
    }
    MOZ_ASSERT(frame->GetParent() == this);

    if (!done) {
      bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK();
      ReflowInlineFrame(aPresContext, aReflowInput, irs, frame, aStatus);
      done = aStatus.IsInlineBreak() ||
             (!reflowingFirstLetter && aStatus.IsIncomplete());
      if (done) {
        if (!irs.mSetParentPointer) {
          break;
        }
        // Keep reparenting the remaining siblings, but don't reflow them.
        nsFrameList* pushedFrames = GetOverflowFrames();
        if (pushedFrames && pushedFrames->FirstChild() == frame) {
          // Don't bother if |frame| was pushed to our overflow list.
          break;
        }
      } else {
        irs.mPrevFrame = frame;
      }
    }
    frame = frame->GetNextSibling();
  }

  // Attempt to pull frames from our next-in-flow until we can't
  if (!done && GetNextInFlow()) {
    while (true) {
      bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK();
      bool isComplete;
      if (!frame) { // Could be non-null if we pulled a first-letter frame and
                    // it created a continuation, since we don't push those.
        frame = PullOneFrame(aPresContext, irs, &isComplete);
      }
#ifdef NOISY_PUSHING
      printf("%p pulled up %p\n", this, frame);
#endif
      if (nullptr == frame) {
        if (!isComplete) {
          aStatus.Reset();
          aStatus.SetIncomplete();
        }
        break;
      }
      ReflowInlineFrame(aPresContext, aReflowInput, irs, frame, aStatus);
      if (aStatus.IsInlineBreak() ||
          (!reflowingFirstLetter && aStatus.IsIncomplete())) {
        break;
      }
      irs.mPrevFrame = frame;
      frame = frame->GetNextSibling();
    }
  }

  NS_ASSERTION(!aStatus.IsComplete() || !GetOverflowFrames(),
               "We can't be complete AND have overflow frames!");

  // If after reflowing our children they take up no area then make
  // sure that we don't either.
  //
  // Note: CSS demands that empty inline elements still affect the
  // line-height calculations. However, continuations of an inline
  // that are empty we force to empty so that things like collapsed
  // whitespace in an inline element don't affect the line-height.
  aMetrics.ISize(lineWM) = lineLayout->EndSpan(this);

  // Compute final width.

  // XXX Note that that the padding start and end are in the frame's
  //     writing mode, but the metrics' inline-size is in the line's
  //     writing mode. This makes sense if the line and frame are both
  //     vertical or both horizontal, but what should happen with
  //     orthogonal inlines?

  // Make sure to not include our start border and padding if we have a prev
  // continuation or if we're in a part of an {ib} split other than the first
  // one.  For box-decoration-break:clone we always include our start border
  // and padding since all continuations have them.
  if ((!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) ||
      boxDecorationBreakClone) {
    aMetrics.ISize(lineWM) += framePadding.IStart(frameWM);
  }

  /*
   * We want to only apply the end border and padding if we're the last
   * continuation and either not in an {ib} split or the last part of it.  To
   * be the last continuation we have to be complete (so that we won't get a
   * next-in-flow) and have no non-fluid continuations on our continuation
   * chain.  For box-decoration-break:clone we always apply the end border and
   * padding since all continuations have them.
   */
  if ((aStatus.IsComplete() &&
       !LastInFlow()->GetNextContinuation() &&
       !FrameIsNonLastInIBSplit()) ||
      boxDecorationBreakClone) {
    aMetrics.ISize(lineWM) += framePadding.IEnd(frameWM);
  }

  nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics,
                                         framePadding, lineWM, frameWM);

  // For now our overflow area is zero. The real value will be
  // computed in |nsLineLayout::RelativePositionFrames|.
  aMetrics.mOverflowAreas.Clear();

#ifdef NOISY_FINAL_SIZE
  ListTag(stdout);
  printf(": metrics=%d,%d ascent=%d\n",
         aMetrics.Width(), aMetrics.Height(), aMetrics.TopAscent());
#endif
}
示例#6
0
void
nsFirstLetterFrame::Reflow(nsPresContext*          aPresContext,
                           ReflowOutput&     aMetrics,
                           const ReflowInput& aReflowInput,
                           nsReflowStatus&          aReflowStatus)
{
  MarkInReflow();
  DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aReflowStatus);

  // Grab overflow list
  DrainOverflowFrames(aPresContext);

  nsIFrame* kid = mFrames.FirstChild();

  // Setup reflow state for our child
  WritingMode wm = aReflowInput.GetWritingMode();
  LogicalSize availSize = aReflowInput.AvailableSize();
  const LogicalMargin& bp = aReflowInput.ComputedLogicalBorderPadding();
  NS_ASSERTION(availSize.ISize(wm) != NS_UNCONSTRAINEDSIZE,
               "should no longer use unconstrained inline size");
  availSize.ISize(wm) -= bp.IStartEnd(wm);
  if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) {
    availSize.BSize(wm) -= bp.BStartEnd(wm);
  }

  WritingMode lineWM = aMetrics.GetWritingMode();
  ReflowOutput kidMetrics(lineWM);

  // Reflow the child
  if (!aReflowInput.mLineLayout) {
    // When there is no lineLayout provided, we provide our own. The
    // only time that the first-letter-frame is not reflowing in a
    // line context is when its floating.
    WritingMode kidWritingMode = WritingModeForLine(wm, kid);
    LogicalSize kidAvailSize = availSize.ConvertTo(kidWritingMode, wm);
    ReflowInput rs(aPresContext, aReflowInput, kid, kidAvailSize);
    nsLineLayout ll(aPresContext, nullptr, &aReflowInput, nullptr, nullptr);

    ll.BeginLineReflow(bp.IStart(wm), bp.BStart(wm),
                       availSize.ISize(wm), NS_UNCONSTRAINEDSIZE,
                       false, true, kidWritingMode,
                       nsSize(aReflowInput.AvailableWidth(),
                              aReflowInput.AvailableHeight()));
    rs.mLineLayout = &ll;
    ll.SetInFirstLetter(true);
    ll.SetFirstLetterStyleOK(true);

    kid->Reflow(aPresContext, kidMetrics, rs, aReflowStatus);

    ll.EndLineReflow();
    ll.SetInFirstLetter(false);

    // In the floating first-letter case, we need to set this ourselves;
    // nsLineLayout::BeginSpan will set it in the other case
    mBaseline = kidMetrics.BlockStartAscent();

    // Place and size the child and update the output metrics
    LogicalSize convertedSize = kidMetrics.Size(lineWM).ConvertTo(wm, lineWM);
    kid->SetRect(nsRect(bp.IStart(wm), bp.BStart(wm),
                        convertedSize.ISize(wm), convertedSize.BSize(wm)));
    kid->FinishAndStoreOverflow(&kidMetrics);
    kid->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);

    convertedSize.ISize(wm) += bp.IStartEnd(wm);
    convertedSize.BSize(wm) += bp.BStartEnd(wm);
    aMetrics.SetSize(wm, convertedSize);
    aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() +
                                 bp.BStart(wm));

    // Ensure that the overflow rect contains the child textframe's
    // overflow rect.
    // Note that if this is floating, the overline/underline drawable
    // area is in the overflow rect of the child textframe.
    aMetrics.UnionOverflowAreasWithDesiredBounds();
    ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);

    FinishAndStoreOverflow(&aMetrics);
  } else {
    // Pretend we are a span and reflow the child frame
    nsLineLayout* ll = aReflowInput.mLineLayout;
    bool          pushedFrame;

    ll->SetInFirstLetter(
      mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter);
    ll->BeginSpan(this, &aReflowInput, bp.IStart(wm),
                  availSize.ISize(wm), &mBaseline);
    ll->ReflowFrame(kid, aReflowStatus, &kidMetrics, pushedFrame);
    NS_ASSERTION(lineWM.IsVertical() == wm.IsVertical(),
                 "we're assuming we can mix sizes between lineWM and wm "
                 "since we shouldn't have orthogonal writing modes within "
                 "a line.");
    aMetrics.ISize(lineWM) = ll->EndSpan(this) + bp.IStartEnd(wm);
    ll->SetInFirstLetter(false);

    if (mStyleContext->StyleTextReset()->mInitialLetterSize != 0.0f) {
      aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() +
                                   bp.BStart(wm));
      aMetrics.BSize(lineWM) = kidMetrics.BSize(lineWM) + bp.BStartEnd(wm);
    } else {
      nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, bp, lineWM, wm);
    }
  }

  if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
    // Create a continuation or remove existing continuations based on
    // the reflow completion status.
    if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
      if (aReflowInput.mLineLayout) {
        aReflowInput.mLineLayout->SetFirstLetterStyleOK(false);
      }
      nsIFrame* kidNextInFlow = kid->GetNextInFlow();
      if (kidNextInFlow) {
        // Remove all of the childs next-in-flows
        kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true);
      }
    } else {
      // Create a continuation for the child frame if it doesn't already
      // have one.
      if (!IsFloating()) {
        CreateNextInFlow(kid);
        // And then push it to our overflow list
        const nsFrameList& overflow = mFrames.RemoveFramesAfter(kid);
        if (overflow.NotEmpty()) {
          SetOverflowFrames(overflow);
        }
      } else if (!kid->GetNextInFlow()) {
        // For floating first letter frames (if a continuation wasn't already
        // created for us) we need to put the continuation with the rest of the
        // text that the first letter frame was made out of.
        nsIFrame* continuation;
        CreateContinuationForFloatingParent(aPresContext, kid,
                                            &continuation, true);
      }
    }
  }

  NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowInput, aMetrics);
}
示例#7
0
/* virtual */ void
nsRubyFrame::Reflow(nsPresContext* aPresContext,
                    ReflowOutput& aDesiredSize,
                    const ReflowInput& aReflowInput,
                    nsReflowStatus& aStatus)
{
  MarkInReflow();
  DO_GLOBAL_REFLOW_COUNT("nsRubyFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");

  if (!aReflowInput.mLineLayout) {
    NS_ASSERTION(aReflowInput.mLineLayout,
                 "No line layout provided to RubyFrame reflow method.");
    return;
  }

  // Grab overflow frames from prev-in-flow and its own.
  MoveInlineOverflowToChildList(
    aReflowInput.mLineLayout->LineContainerFrame());

  // Clear leadings
  mLeadings.Reset();

  // Since the ruby base container is going to reflow not only the ruby
  // base frames, but also the ruby text frames, and then *afterwards*
  // we're going to reflow the ruby text containers (which do not reflow
  // their children), we need to transfer NS_FRAME_IS_DIRTY status from
  // the ruby text containers to their child ruby texts now, both so
  // that the ruby texts are marked dirty if needed, and so that the
  // ruby text container doesn't mark the ruby text frames dirty *after*
  // they're reflowed and leave dirty bits in a clean tree (suppressing
  // future reflows, due to lack of a queued reflow to clean them).
  for (nsIFrame* child : PrincipalChildList()) {
    if (child->HasAnyStateBits(NS_FRAME_IS_DIRTY) &&
        child->IsRubyTextContainerFrame()) {
      for (nsIFrame* grandchild : child->PrincipalChildList()) {
        grandchild->AddStateBits(NS_FRAME_IS_DIRTY);
      }
      // Replace NS_FRAME_IS_DIRTY with NS_FRAME_HAS_DIRTY_CHILDREN so
      // we still have a dirty marking, but one that we won't transfer
      // to children again.
      child->RemoveStateBits(NS_FRAME_IS_DIRTY);
      child->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
    }
  }

  // Begin the span for the ruby frame
  WritingMode frameWM = aReflowInput.GetWritingMode();
  WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode();
  LogicalMargin borderPadding = aReflowInput.ComputedLogicalBorderPadding();
  nscoord startEdge = 0;
  const bool boxDecorationBreakClone =
    StyleBorder()->mBoxDecorationBreak == StyleBoxDecorationBreak::Clone;
  if (boxDecorationBreakClone || !GetPrevContinuation()) {
    startEdge = borderPadding.IStart(frameWM);
  }
  NS_ASSERTION(aReflowInput.AvailableISize() != NS_UNCONSTRAINEDSIZE,
               "should no longer use available widths");
  nscoord availableISize = aReflowInput.AvailableISize();
  availableISize -= startEdge + borderPadding.IEnd(frameWM);
  aReflowInput.mLineLayout->BeginSpan(this, &aReflowInput,
                                      startEdge, availableISize, &mBaseline);

  for (RubySegmentEnumerator e(this); !e.AtEnd(); e.Next()) {
    ReflowSegment(aPresContext, aReflowInput, e.GetBaseContainer(), aStatus);

    if (aStatus.IsInlineBreak()) {
      // A break occurs when reflowing the segment.
      // Don't continue reflowing more segments.
      break;
    }
  }

  ContinuationTraversingState pullState(this);
  while (aStatus.IsEmpty()) {
    nsRubyBaseContainerFrame* baseContainer =
      PullOneSegment(aReflowInput.mLineLayout, pullState);
    if (!baseContainer) {
      // No more continuations after, finish now.
      break;
    }
    ReflowSegment(aPresContext, aReflowInput, baseContainer, aStatus);
  }
  // We never handle overflow in ruby.
  MOZ_ASSERT(!aStatus.IsOverflowIncomplete());

  aDesiredSize.ISize(lineWM) = aReflowInput.mLineLayout->EndSpan(this);
  if (boxDecorationBreakClone || !GetPrevContinuation()) {
    aDesiredSize.ISize(lineWM) += borderPadding.IStart(frameWM);
  }
  if (boxDecorationBreakClone || aStatus.IsComplete()) {
    aDesiredSize.ISize(lineWM) += borderPadding.IEnd(frameWM);
  }

  // Update descendant leadings of ancestor ruby base container.
  if (nsRubyBaseContainerFrame* rbc = FindRubyBaseContainerAncestor(this)) {
    rbc->UpdateDescendantLeadings(mLeadings);
  }

  nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize,
                                         borderPadding, lineWM, frameWM);
}
/* virtual */ void
nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext,
                                 ReflowOutput& aDesiredSize,
                                 const ReflowInput& aReflowInput,
                                 nsReflowStatus& aStatus)
{
  MarkInReflow();
  DO_GLOBAL_REFLOW_COUNT("nsRubyBaseContainerFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
  aStatus = NS_FRAME_COMPLETE;

  if (!aReflowInput.mLineLayout) {
    NS_ASSERTION(
      aReflowInput.mLineLayout,
      "No line layout provided to RubyBaseContainerFrame reflow method.");
    return;
  }

  mDescendantLeadings.Reset();

  MoveOverflowToChildList();
  // Ask text containers to drain overflows
  AutoRubyTextContainerArray textContainers(this);
  const uint32_t rtcCount = textContainers.Length();
  for (uint32_t i = 0; i < rtcCount; i++) {
    textContainers[i]->MoveOverflowToChildList();
  }

  WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode();
  LogicalSize availSize(lineWM, aReflowInput.AvailableISize(),
                        aReflowInput.AvailableBSize());

  // We have a reflow state and a line layout for each RTC.
  // They are conceptually the state of the RTCs, but we don't actually
  // reflow those RTCs in this code. These two arrays are holders of
  // the reflow states and line layouts.
  // Since there are pointers refer to reflow states and line layouts,
  // it is necessary to guarantee that they won't be moved. For this
  // reason, they are wrapped in UniquePtr here.
  AutoTArray<UniquePtr<ReflowInput>, RTC_ARRAY_SIZE> reflowInputs;
  AutoTArray<UniquePtr<nsLineLayout>, RTC_ARRAY_SIZE> lineLayouts;
  reflowInputs.SetCapacity(rtcCount);
  lineLayouts.SetCapacity(rtcCount);

  // Begin the line layout for each ruby text container in advance.
  bool hasSpan = false;
  for (uint32_t i = 0; i < rtcCount; i++) {
    nsRubyTextContainerFrame* textContainer = textContainers[i];
    if (textContainer->IsSpanContainer()) {
      hasSpan = true;
    }

    ReflowInput* reflowInput = new ReflowInput(
      aPresContext, *aReflowInput.mParentReflowInput, textContainer,
      availSize.ConvertTo(textContainer->GetWritingMode(), lineWM));
    reflowInputs.AppendElement(reflowInput);
    nsLineLayout* lineLayout = new nsLineLayout(aPresContext,
                                                reflowInput->mFloatManager,
                                                reflowInput, nullptr,
                                                aReflowInput.mLineLayout);
    lineLayout->SetSuppressLineWrap(true);
    lineLayouts.AppendElement(lineLayout);

    // Line number is useless for ruby text
    // XXX nullptr here may cause problem, see comments for
    //     nsLineLayout::mBlockRI and nsLineLayout::AddFloat
    lineLayout->Init(nullptr, reflowInput->CalcLineHeight(), -1);
    reflowInput->mLineLayout = lineLayout;

    // Border and padding are suppressed on ruby text containers.
    // If the writing mode is vertical-rl, the horizontal position of
    // rt frames will be updated when reflowing this text container,
    // hence leave container size 0 here for now.
    lineLayout->BeginLineReflow(0, 0, reflowInput->ComputedISize(),
                                NS_UNCONSTRAINEDSIZE,
                                false, false, lineWM, nsSize(0, 0));
    lineLayout->AttachRootFrameToBaseLineLayout();
  }

  aReflowInput.mLineLayout->BeginSpan(this, &aReflowInput,
                                      0, aReflowInput.AvailableISize(),
                                      &mBaseline);

  bool allowInitialLineBreak, allowLineBreak;
  GetIsLineBreakAllowed(this, aReflowInput.mLineLayout->LineIsBreakable(),
                        &allowInitialLineBreak, &allowLineBreak);

  nscoord isize = 0;
  // Reflow columns excluding any span
  RubyReflowInput reflowInput = {
    allowInitialLineBreak, allowLineBreak && !hasSpan,
    textContainers, aReflowInput, reflowInputs
  };
  isize = ReflowColumns(reflowInput, aStatus);
  DebugOnly<nscoord> lineSpanSize = aReflowInput.mLineLayout->EndSpan(this);
  aDesiredSize.ISize(lineWM) = isize;
  // When there are no frames inside the ruby base container, EndSpan
  // will return 0. However, in this case, the actual width of the
  // container could be non-zero because of non-empty ruby annotations.
  // XXX When bug 765861 gets fixed, this warning should be upgraded.
  NS_WARNING_ASSERTION(
    NS_INLINE_IS_BREAK(aStatus) || isize == lineSpanSize || mFrames.IsEmpty(),
    "bad isize");

  // If there exists any span, the columns must either be completely
  // reflowed, or be not reflowed at all.
  MOZ_ASSERT(NS_INLINE_IS_BREAK_BEFORE(aStatus) ||
             NS_FRAME_IS_COMPLETE(aStatus) || !hasSpan);
  if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) &&
      NS_FRAME_IS_COMPLETE(aStatus) && hasSpan) {
    // Reflow spans
    RubyReflowInput reflowInput = {
      false, false, textContainers, aReflowInput, reflowInputs
    };
    nscoord spanISize = ReflowSpans(reflowInput);
    isize = std::max(isize, spanISize);
  }

  for (uint32_t i = 0; i < rtcCount; i++) {
    // It happens before the ruby text container is reflowed, and that
    // when it is reflowed, it will just use this size.
    nsRubyTextContainerFrame* textContainer = textContainers[i];
    nsLineLayout* lineLayout = lineLayouts[i].get();

    RubyUtils::ClearReservedISize(textContainer);
    nscoord rtcISize = lineLayout->GetCurrentICoord();
    // Only span containers and containers with collapsed annotations
    // need reserving isize. For normal ruby text containers, their
    // children will be expanded properly. We only need to expand their
    // own size.
    if (!textContainer->IsSpanContainer()) {
      rtcISize = isize;
    } else if (isize > rtcISize) {
      RubyUtils::SetReservedISize(textContainer, isize - rtcISize);
    }

    lineLayout->VerticalAlignLine();
    textContainer->SetISize(rtcISize);
    lineLayout->EndLineReflow();
  }

  // Border and padding are suppressed on ruby base container,
  // create a fake borderPadding for setting BSize.
  WritingMode frameWM = aReflowInput.GetWritingMode();
  LogicalMargin borderPadding(frameWM);
  nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize,
                                         borderPadding, lineWM, frameWM);
}