// Only reflow the selected child ...
void
nsMathMLSelectedFrame::Reflow(nsPresContext*          aPresContext,
                              ReflowOutput&     aDesiredSize,
                              const ReflowInput& aReflowInput,
                              nsReflowStatus&          aStatus)
{
  MarkInReflow();
  mPresentationData.flags &= ~NS_MATHML_ERROR;
  aStatus.Reset();
  aDesiredSize.ClearSize();
  aDesiredSize.SetBlockStartAscent(0);
  mBoundingMetrics = nsBoundingMetrics();
  nsIFrame* childFrame = GetSelectedFrame();
  if (childFrame) {
    WritingMode wm = childFrame->GetWritingMode();
    LogicalSize availSize = aReflowInput.ComputedSize(wm);
    availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
    ReflowInput childReflowInput(aPresContext, aReflowInput,
                                       childFrame, availSize);
    ReflowChild(childFrame, aPresContext, aDesiredSize,
                childReflowInput, aStatus);
    SaveReflowAndBoundingMetricsFor(childFrame, aDesiredSize,
                                    aDesiredSize.mBoundingMetrics);
    mBoundingMetrics = aDesiredSize.mBoundingMetrics;
  }
  FinalizeReflow(aReflowInput.mRenderingContext->GetDrawTarget(), aDesiredSize);
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
}
void
nsTableColGroupFrame::Reflow(nsPresContext*          aPresContext,
                             ReflowOutput&     aDesiredSize,
                             const ReflowInput& aReflowInput,
                             nsReflowStatus&          aStatus)
{
  MarkInReflow();
  DO_GLOBAL_REFLOW_COUNT("nsTableColGroupFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
  NS_ASSERTION(nullptr!=mContent, "bad state -- null content for frame");
  
  const nsStyleVisibility* groupVis = StyleVisibility();
  bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
  if (collapseGroup) {
    GetTableFrame()->SetNeedToCollapse(true);
  }
  // for every content child that (is a column thingy and does not already have a frame)
  // create a frame and adjust it's style
  
  for (nsIFrame *kidFrame = mFrames.FirstChild(); kidFrame;
       kidFrame = kidFrame->GetNextSibling()) {
    // Give the child frame a chance to reflow, even though we know it'll have 0 size
    ReflowOutput kidSize(aReflowInput);
    ReflowInput kidReflowInput(aPresContext, aReflowInput, kidFrame,
                                     LogicalSize(kidFrame->GetWritingMode()));

    nsReflowStatus status;
    ReflowChild(kidFrame, aPresContext, kidSize, kidReflowInput, 0, 0, 0, status);
    FinishReflowChild(kidFrame, aPresContext, kidSize, nullptr, 0, 0, 0);
  }

  aDesiredSize.ClearSize();
  aStatus.Reset();
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
}
Ejemplo n.º 3
0
/* virtual */ void
nsBackdropFrame::Reflow(nsPresContext* aPresContext,
                        ReflowOutput& aDesiredSize,
                        const ReflowInput& aReflowInput,
                        nsReflowStatus& aStatus)
{
  MarkInReflow();
  DO_GLOBAL_REFLOW_COUNT("nsBackdropFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);

  // Note that this frame is a child of the viewport frame.
  WritingMode wm = aReflowInput.GetWritingMode();
  LogicalMargin borderPadding = aReflowInput.ComputedLogicalBorderPadding();
  nscoord isize = aReflowInput.ComputedISize() + borderPadding.IStartEnd(wm);
  nscoord bsize = aReflowInput.ComputedBSize() + borderPadding.BStartEnd(wm);
  aDesiredSize.SetSize(wm, LogicalSize(wm, isize, bsize));
  aStatus.Reset();
}
Ejemplo n.º 4
0
void
nsInlineFrame::ReflowInlineFrame(nsPresContext* aPresContext,
                                 const ReflowInput& aReflowInput,
                                 InlineReflowInput& irs,
                                 nsIFrame* aFrame,
                                 nsReflowStatus& aStatus)
{
  nsLineLayout* lineLayout = aReflowInput.mLineLayout;
  bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK();
  bool pushedFrame;
  aStatus.Reset();
  lineLayout->ReflowFrame(aFrame, aStatus, nullptr, pushedFrame);

  if (aStatus.IsInlineBreakBefore()) {
    if (aFrame != mFrames.FirstChild()) {
      // Change break-before status into break-after since we have
      // already placed at least one child frame. This preserves the
      // break-type so that it can be propagated upward.
      StyleClear oldBreakType = aStatus.BreakType();
      aStatus.Reset();
      aStatus.SetIncomplete();
      aStatus.SetInlineLineBreakAfter(oldBreakType);
      PushFrames(aPresContext, aFrame, irs.mPrevFrame, irs);
    }
    else {
      // Preserve reflow status when breaking-before our first child
      // and propagate it upward without modification.
    }
    return;
  }

  // Create a next-in-flow if needed.
  if (!aStatus.IsFullyComplete()) {
    CreateNextInFlow(aFrame);
  }

  if (aStatus.IsInlineBreakAfter()) {
    nsIFrame* nextFrame = aFrame->GetNextSibling();
    if (nextFrame) {
      aStatus.SetIncomplete();
      PushFrames(aPresContext, nextFrame, aFrame, irs);
    }
    else {
      // We must return an incomplete status if there are more child
      // frames remaining in a next-in-flow that follows this frame.
      nsInlineFrame* nextInFlow = static_cast<nsInlineFrame*>(GetNextInFlow());
      while (nextInFlow) {
        if (nextInFlow->mFrames.NotEmpty()) {
          aStatus.SetIncomplete();
          break;
        }
        nextInFlow = static_cast<nsInlineFrame*>(nextInFlow->GetNextInFlow());
      }
    }
    return;
  }

  if (!aStatus.IsFullyComplete() && !reflowingFirstLetter) {
    nsIFrame* nextFrame = aFrame->GetNextSibling();
    if (nextFrame) {
      PushFrames(aPresContext, nextFrame, aFrame, irs);
    }
  }
}
Ejemplo n.º 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
}
Ejemplo n.º 6
0
void
nsInlineFrame::Reflow(nsPresContext*          aPresContext,
                      ReflowOutput&     aMetrics,
                      const ReflowInput& aReflowInput,
                      nsReflowStatus&          aStatus)
{
  MarkInReflow();
  DO_GLOBAL_REFLOW_COUNT("nsInlineFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");

  if (nullptr == aReflowInput.mLineLayout) {
    NS_ERROR("must have non-null aReflowInput.mLineLayout");
    return;
  }
  if (IsFrameTreeTooDeep(aReflowInput, aMetrics, aStatus)) {
    return;
  }

  bool    lazilySetParentPointer = false;

   // Check for an overflow list with our prev-in-flow
  nsInlineFrame* prevInFlow = (nsInlineFrame*)GetPrevInFlow();
  if (prevInFlow) {
    AutoFrameListPtr prevOverflowFrames(aPresContext,
                                        prevInFlow->StealOverflowFrames());
    if (prevOverflowFrames) {
      // When pushing and pulling frames we need to check for whether any
      // views need to be reparented.
      nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames, prevInFlow,
                                              this);

      // Check if we should do the lazilySetParentPointer optimization.
      // Only do it in simple cases where we're being reflowed for the
      // first time, nothing (e.g. bidi resolution) has already given
      // us children, and there's no next-in-flow, so all our frames
      // will be taken from prevOverflowFrames.
      if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && mFrames.IsEmpty() &&
          !GetNextInFlow()) {
        // If our child list is empty, just put the new frames into it.
        // Note that we don't set the parent pointer for the new frames. Instead wait
        // to do this until we actually reflow the frame. If the overflow list contains
        // thousands of frames this is a big performance issue (see bug #5588)
        mFrames.SetFrames(*prevOverflowFrames);
        lazilySetParentPointer = true;
      } else {
        // Insert the new frames at the beginning of the child list
        // and set their parent pointer
        const nsFrameList::Slice& newFrames =
          mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
        // If our prev in flow was under the first continuation of a first-line
        // frame then we need to reparent the style contexts to remove the
        // the special first-line styling. In the lazilySetParentPointer case
        // we reparent the style contexts when we set their parents in
        // nsInlineFrame::ReflowFrames and nsInlineFrame::ReflowInlineFrame.
        if (aReflowInput.mLineLayout->GetInFirstLine()) {
          ReparentChildListStyle(aPresContext, newFrames, this);
        }
      }
    }
  }

  // It's also possible that we have an overflow list for ourselves
#ifdef DEBUG
  if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
    // If it's our initial reflow, then we should not have an overflow list.
    // However, add an assertion in case we get reflowed more than once with
    // the initial reflow reason
    nsFrameList* overflowFrames = GetOverflowFrames();
    NS_ASSERTION(!overflowFrames || overflowFrames->IsEmpty(),
                 "overflow list is not empty for initial reflow");
  }
#endif
  if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
    DrainFlags flags =
      lazilySetParentPointer ? eDontReparentFrames : DrainFlags(0);
    if (aReflowInput.mLineLayout->GetInFirstLine()) {
      flags = DrainFlags(flags | eInFirstLine);
    }
    DrainSelfOverflowListInternal(flags);
  }

  // Set our own reflow state (additional state above and beyond aReflowInput).
  InlineReflowInput irs;
  irs.mPrevFrame = nullptr;
  irs.mLineContainer = aReflowInput.mLineLayout->LineContainerFrame();
  irs.mLineLayout = aReflowInput.mLineLayout;
  irs.mNextInFlow = (nsInlineFrame*) GetNextInFlow();
  irs.mSetParentPointer = lazilySetParentPointer;

  if (mFrames.IsEmpty()) {
    // Try to pull over one frame before starting so that we know
    // whether we have an anonymous block or not.
    bool complete;
    (void) PullOneFrame(aPresContext, irs, &complete);
  }

  ReflowFrames(aPresContext, aReflowInput, irs, aMetrics, aStatus);

  ReflowAbsoluteFrames(aPresContext, aMetrics, aReflowInput, aStatus);

  // Note: the line layout code will properly compute our
  // overflow-rect state for us.

  NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
}
Ejemplo n.º 7
0
void
nsFirstLineFrame::Reflow(nsPresContext* aPresContext,
                         ReflowOutput& aMetrics,
                         const ReflowInput& aReflowInput,
                         nsReflowStatus& aStatus)
{
  MarkInReflow();
  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");

  if (nullptr == aReflowInput.mLineLayout) {
    return;  // XXX does this happen? why?
  }

  // Check for an overflow list with our prev-in-flow
  nsFirstLineFrame* prevInFlow = (nsFirstLineFrame*)GetPrevInFlow();
  if (prevInFlow) {
    AutoFrameListPtr prevOverflowFrames(aPresContext,
                                        prevInFlow->StealOverflowFrames());
    if (prevOverflowFrames) {
      // Reparent the new frames and their style contexts.
      const nsFrameList::Slice& newFrames =
        mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
      ReparentChildListStyle(aPresContext, newFrames, this);
    }
  }

  // It's also possible that we have an overflow list for ourselves.
  DrainSelfOverflowList();

  // Set our own reflow state (additional state above and beyond aReflowInput).
  InlineReflowInput irs;
  irs.mPrevFrame = nullptr;
  irs.mLineContainer = aReflowInput.mLineLayout->LineContainerFrame();
  irs.mLineLayout = aReflowInput.mLineLayout;
  irs.mNextInFlow = (nsInlineFrame*) GetNextInFlow();

  bool wasEmpty = mFrames.IsEmpty();
  if (wasEmpty) {
    // Try to pull over one frame before starting so that we know
    // whether we have an anonymous block or not.
    bool complete;
    PullOneFrame(aPresContext, irs, &complete);
  }

  if (nullptr == GetPrevInFlow()) {
    // XXX This is pretty sick, but what we do here is to pull-up, in
    // advance, all of the next-in-flows children. We re-resolve their
    // style while we are at at it so that when we reflow they have
    // the right style.
    //
    // All of this is so that text-runs reflow properly.
    irs.mPrevFrame = mFrames.LastChild();
    for (;;) {
      bool complete;
      nsIFrame* frame = PullOneFrame(aPresContext, irs, &complete);
      if (!frame) {
        break;
      }
      irs.mPrevFrame = frame;
    }
    irs.mPrevFrame = nullptr;
  }

  NS_ASSERTION(!aReflowInput.mLineLayout->GetInFirstLine(),
               "Nested first-line frames? BOGUS");
  aReflowInput.mLineLayout->SetInFirstLine(true);
  ReflowFrames(aPresContext, aReflowInput, irs, aMetrics, aStatus);
  aReflowInput.mLineLayout->SetInFirstLine(false);

  ReflowAbsoluteFrames(aPresContext, aMetrics, aReflowInput, aStatus);

  // Note: the line layout code will properly compute our overflow state for us
}
/*
 * Note: we largely position/size out our children (page frames) using
 * \*physical\* x/y/width/height values, because the print preview UI is always
 * arranged in the same orientation, regardless of writing mode.
 */
void
nsSimplePageSequenceFrame::Reflow(nsPresContext*     aPresContext,
                                  ReflowOutput&      aDesiredSize,
                                  const ReflowInput& aReflowInput,
                                  nsReflowStatus&    aStatus)
{
  MarkInReflow();
  NS_PRECONDITION(aPresContext->IsRootPaginatedDocument(),
                  "A Page Sequence is only for real pages");
  DO_GLOBAL_REFLOW_COUNT("nsSimplePageSequenceFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
  NS_FRAME_TRACE_REFLOW_IN("nsSimplePageSequenceFrame::Reflow");

  // Don't do incremental reflow until we've taught tables how to do
  // it right in paginated mode.
  if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
    // Return our desired size
    SetDesiredSize(aDesiredSize, aReflowInput, mSize.width, mSize.height);
    aDesiredSize.SetOverflowAreasToDesiredBounds();
    FinishAndStoreOverflow(&aDesiredSize);

    if (GetRect().Width() != aDesiredSize.Width()) {
      // Our width is changing; we need to re-center our children (our pages).
      for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
        nsIFrame* child = e.get();
        nsMargin pageCSSMargin = child->GetUsedMargin();
        nscoord centeringMargin =
          ComputeCenteringMargin(aReflowInput.ComputedWidth(),
                                 child->GetRect().Width(),
                                 pageCSSMargin);
        nscoord newX = pageCSSMargin.left + centeringMargin;

        // Adjust the child's x-position:
        child->MovePositionBy(nsPoint(newX - child->GetNormalPosition().x, 0));
      }
    }
    return;
  }

  // See if we can get a Print Settings from the Context
  if (!mPageData->mPrintSettings &&
      aPresContext->Medium() == nsGkAtoms::print) {
      mPageData->mPrintSettings = aPresContext->GetPrintSettings();
  }

  // now get out margins & edges
  if (mPageData->mPrintSettings) {
    nsIntMargin unwriteableTwips;
    mPageData->mPrintSettings->GetUnwriteableMarginInTwips(unwriteableTwips);
    NS_ASSERTION(unwriteableTwips.left  >= 0 && unwriteableTwips.top >= 0 &&
                 unwriteableTwips.right >= 0 && unwriteableTwips.bottom >= 0,
                 "Unwriteable twips should be non-negative");

    nsIntMargin marginTwips;
    mPageData->mPrintSettings->GetMarginInTwips(marginTwips);
    mMargin = aPresContext->CSSTwipsToAppUnits(marginTwips + unwriteableTwips);

    int16_t printType;
    mPageData->mPrintSettings->GetPrintRange(&printType);
    mPrintRangeType = printType;

    nsIntMargin edgeTwips;
    mPageData->mPrintSettings->GetEdgeInTwips(edgeTwips);

    // sanity check the values. three inches are sometimes needed
    int32_t inchInTwips = NS_INCHES_TO_INT_TWIPS(3.0);
    edgeTwips.top    = clamped(edgeTwips.top,    0, inchInTwips);
    edgeTwips.bottom = clamped(edgeTwips.bottom, 0, inchInTwips);
    edgeTwips.left   = clamped(edgeTwips.left,   0, inchInTwips);
    edgeTwips.right  = clamped(edgeTwips.right,  0, inchInTwips);

    mPageData->mEdgePaperMargin =
      aPresContext->CSSTwipsToAppUnits(edgeTwips + unwriteableTwips);
  }

  // *** Special Override ***
  // If this is a sub-sdoc (meaning it doesn't take the whole page)
  // and if this Document is in the upper left hand corner
  // we need to suppress the top margin or it will reflow too small

  nsSize pageSize = aPresContext->GetPageSize();

  mPageData->mReflowSize = pageSize;
  mPageData->mReflowMargin = mMargin;

  // We use the CSS "margin" property on the -moz-page pseudoelement
  // to determine the space between each page in print preview.
  // Keep a running y-offset for each page.
  nscoord y = 0;
  nscoord maxXMost = 0;

  // Tile the pages vertically
  ReflowOutput kidSize(aReflowInput);
  for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
    nsIFrame* kidFrame = e.get();
    // Set the shared data into the page frame before reflow
    nsPageFrame * pf = static_cast<nsPageFrame*>(kidFrame);
    pf->SetSharedPageData(mPageData);

    // Reflow the page
    ReflowInput kidReflowInput(aPresContext, aReflowInput, kidFrame,
                               LogicalSize(kidFrame->GetWritingMode(),
                                                 pageSize));
    nsReflowStatus  status;

    kidReflowInput.SetComputedISize(kidReflowInput.AvailableISize());
    //kidReflowInput.SetComputedHeight(kidReflowInput.AvailableHeight());
    PR_PL(("AV ISize: %d   BSize: %d\n",
           kidReflowInput.AvailableISize(),
           kidReflowInput.AvailableBSize()));

    nsMargin pageCSSMargin = kidReflowInput.ComputedPhysicalMargin();
    y += pageCSSMargin.top;

    nscoord x = pageCSSMargin.left;

    // Place and size the page.
    ReflowChild(kidFrame, aPresContext, kidSize, kidReflowInput, x, y, 0, status);

    // If the page is narrower than our width, then center it horizontally:
    x += ComputeCenteringMargin(aReflowInput.ComputedWidth(),
                                kidSize.Width(), pageCSSMargin);

    FinishReflowChild(kidFrame, aPresContext, kidSize, nullptr, x, y, 0);
    y += kidSize.Height();
    y += pageCSSMargin.bottom;

    maxXMost = std::max(maxXMost, x + kidSize.Width() + pageCSSMargin.right);

    // Is the page complete?
    nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();

    if (status.IsFullyComplete()) {
      NS_ASSERTION(!kidNextInFlow, "bad child flow list");
    } else if (!kidNextInFlow) {
      // The page isn't complete and it doesn't have a next-in-flow, so
      // create a continuing page.
      nsIFrame* continuingPage = aPresContext->PresShell()->FrameConstructor()->
        CreateContinuingFrame(aPresContext, kidFrame, this);

      // Add it to our child list
      mFrames.InsertFrame(nullptr, kidFrame, continuingPage);
    }
  }

  // Get Total Page Count
  // XXXdholbert technically we could calculate this in the loop above,
  // instead of needing a separate walk.
  int32_t pageTot = mFrames.GetLength();

  // Set Page Number Info
  int32_t pageNum = 1;
  for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
    MOZ_ASSERT(e.get()->IsPageFrame(),
               "only expecting nsPageFrame children. Other children will make "
               "this static_cast bogus & probably violate other assumptions");
    nsPageFrame* pf = static_cast<nsPageFrame*>(e.get());
    pf->SetPageNumInfo(pageNum, pageTot);
    pageNum++;
  }

  nsAutoString formattedDateString;
  PRTime now = PR_Now();
  if (NS_SUCCEEDED(DateTimeFormat::FormatPRTime(kDateFormatShort,
                                                kTimeFormatNoSeconds,
                                                now,
                                                formattedDateString))) {
    SetDateTimeStr(formattedDateString);
  }

  // Return our desired size
  // Adjust the reflow size by PrintPreviewScale so the scrollbars end up the
  // correct size
  SetDesiredSize(aDesiredSize, aReflowInput, maxXMost, y);

  aDesiredSize.SetOverflowAreasToDesiredBounds();
  FinishAndStoreOverflow(&aDesiredSize);

  // cache the size so we can set the desired size
  // for the other reflows that happen
  mSize.width  = maxXMost;
  mSize.height = y;

  NS_FRAME_TRACE_REFLOW_OUT("nsSimplePageSequeceFrame::Reflow", aStatus);
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
}
Ejemplo n.º 9
0
void
nsTableCellFrame::Reflow(nsPresContext*           aPresContext,
                         ReflowOutput&     aDesiredSize,
                         const ReflowInput& aReflowInput,
                         nsReflowStatus&          aStatus)
{
  MarkInReflow();
  DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);

  if (aReflowInput.mFlags.mSpecialBSizeReflow) {
    FirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW);
  }

  // see if a special bsize reflow needs to occur due to having a pct height
  nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput);

  aStatus.Reset();
  WritingMode wm = aReflowInput.GetWritingMode();
  LogicalSize availSize(wm, aReflowInput.AvailableISize(),
                            aReflowInput.AvailableBSize());

  LogicalMargin borderPadding = aReflowInput.ComputedLogicalPadding();
  LogicalMargin border = GetBorderWidth(wm);
  borderPadding += border;

  // reduce available space by insets, if we're in a constrained situation
  availSize.ISize(wm) -= borderPadding.IStartEnd(wm);
  if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) {
    availSize.BSize(wm) -= borderPadding.BStartEnd(wm);
  }

  // Try to reflow the child into the available space. It might not
  // fit or might need continuing.
  if (availSize.BSize(wm) < 0) {
    availSize.BSize(wm) = 1;
  }

  ReflowOutput kidSize(wm, aDesiredSize.mFlags);
  kidSize.ClearSize();
  SetPriorAvailISize(aReflowInput.AvailableISize());
  nsIFrame* firstKid = mFrames.FirstChild();
  NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame");
  nsTableFrame* tableFrame = GetTableFrame();

  if (aReflowInput.mFlags.mSpecialBSizeReflow) {
    const_cast<ReflowInput&>(aReflowInput).
      SetComputedBSize(BSize(wm) - borderPadding.BStartEnd(wm));
    DISPLAY_REFLOW_CHANGE();
  }
  else if (aPresContext->IsPaginated()) {
    nscoord computedUnpaginatedBSize =
      CalcUnpaginatedBSize((nsTableCellFrame&)*this,
                           *tableFrame, borderPadding.BStartEnd(wm));
    if (computedUnpaginatedBSize > 0) {
      const_cast<ReflowInput&>(aReflowInput).SetComputedBSize(computedUnpaginatedBSize);
      DISPLAY_REFLOW_CHANGE();
    }
  }
  else {
    SetHasPctOverBSize(false);
  }

  WritingMode kidWM = firstKid->GetWritingMode();
  ReflowInput kidReflowInput(aPresContext, aReflowInput, firstKid,
                                   availSize.ConvertTo(kidWM, wm));

  // Don't be a percent height observer if we're in the middle of
  // special-bsize reflow, in case we get an accidental NotifyPercentBSize()
  // call (which we shouldn't honor during special-bsize reflow)
  if (!aReflowInput.mFlags.mSpecialBSizeReflow) {
    // mPercentBSizeObserver is for children of cells in quirks mode,
    // but only those than are tables in standards mode.  NeedsToObserve
    // will determine how far this is propagated to descendants.
    kidReflowInput.mPercentBSizeObserver = this;
  }
  // Don't propagate special bsize reflow state to our kids
  kidReflowInput.mFlags.mSpecialBSizeReflow = false;

  if (aReflowInput.mFlags.mSpecialBSizeReflow ||
      FirstInFlow()->HasAnyStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) {
    // We need to force the kid to have mBResize set if we've had a
    // special reflow in the past, since the non-special reflow needs to
    // resize back to what it was without the special bsize reflow.
    kidReflowInput.SetBResize(true);
  }

  nsSize containerSize =
    aReflowInput.ComputedSizeAsContainerIfConstrained();

  LogicalPoint kidOrigin(wm, borderPadding.IStart(wm),
                         borderPadding.BStart(wm));
  nsRect origRect = firstKid->GetRect();
  nsRect origVisualOverflow = firstKid->GetVisualOverflowRect();
  bool firstReflow = firstKid->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);

  ReflowChild(firstKid, aPresContext, kidSize, kidReflowInput,
              wm, kidOrigin, containerSize, 0, aStatus);
  if (aStatus.IsOverflowIncomplete()) {
    // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually handle it
    //XXX should paginate overflow as overflow, but not in this patch (bug 379349)
    aStatus.SetIncomplete();
    printf("Set table cell incomplete %p\n", static_cast<void*>(this));
  }

  // XXXbz is this invalidate actually needed, really?
  if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
    InvalidateFrameSubtree();
  }

#ifdef DEBUG
  DebugCheckChildSize(firstKid, kidSize);
#endif

  // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode
  // see testcase "emptyCells.html"
  nsIFrame* prevInFlow = GetPrevInFlow();
  bool isEmpty;
  if (prevInFlow) {
    isEmpty = static_cast<nsTableCellFrame*>(prevInFlow)->GetContentEmpty();
  } else {
    isEmpty = !CellHasVisibleContent(kidSize.Height(), tableFrame, firstKid);
  }
  SetContentEmpty(isEmpty);

  // Place the child
  FinishReflowChild(firstKid, aPresContext, kidSize, &kidReflowInput,
                    wm, kidOrigin, containerSize, 0);

  nsTableFrame::InvalidateTableFrame(firstKid, origRect, origVisualOverflow,
                                     firstReflow);

  // first, compute the bsize which can be set w/o being restricted by
  // available bsize
  LogicalSize cellSize(wm);
  cellSize.BSize(wm) = kidSize.BSize(wm);

  if (NS_UNCONSTRAINEDSIZE != cellSize.BSize(wm)) {
    cellSize.BSize(wm) += borderPadding.BStartEnd(wm);
  }

  // next determine the cell's isize
  cellSize.ISize(wm) = kidSize.ISize(wm);      // at this point, we've factored in the cell's style attributes

  // factor in border and padding
  if (NS_UNCONSTRAINEDSIZE != cellSize.ISize(wm)) {
    cellSize.ISize(wm) += borderPadding.IStartEnd(wm);
  }

  // set the cell's desired size and max element size
  aDesiredSize.SetSize(wm, cellSize);

  // the overflow area will be computed when BlockDirAlignChild() gets called

  if (aReflowInput.mFlags.mSpecialBSizeReflow) {
    if (aDesiredSize.BSize(wm) > BSize(wm)) {
      // set a bit indicating that the pct bsize contents exceeded
      // the height that they could honor in the pass 2 reflow
      SetHasPctOverBSize(true);
    }
    if (NS_UNCONSTRAINEDSIZE == aReflowInput.AvailableBSize()) {
      aDesiredSize.BSize(wm) = BSize(wm);
    }
  }

  // If our parent is in initial reflow, it'll handle invalidating our
  // entire overflow rect.
  if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW) &&
      nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
    InvalidateFrame();
  }

  // remember the desired size for this reflow
  SetDesiredSize(aDesiredSize);

  // Any absolutely-positioned children will get reflowed in
  // nsFrame::FixupPositionedTableParts in another pass, so propagate our
  // dirtiness to them before our parent clears our dirty bits.
  PushDirtyBitToAbsoluteFrames();

  NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
}
Ejemplo n.º 10
0
void
nsMathMLmrootFrame::Reflow(nsPresContext*          aPresContext,
                           ReflowOutput&     aDesiredSize,
                           const ReflowInput& aReflowInput,
                           nsReflowStatus&          aStatus)
{
  MarkInReflow();
  nsReflowStatus childStatus;

  mPresentationData.flags &= ~NS_MATHML_ERROR;
  aDesiredSize.ClearSize();
  aDesiredSize.SetBlockStartAscent(0);

  nsBoundingMetrics bmSqr, bmBase, bmIndex;
  DrawTarget* drawTarget = aReflowInput.mRenderingContext->GetDrawTarget();

  //////////////////
  // Reflow Children

  int32_t count = 0;
  nsIFrame* baseFrame = nullptr;
  nsIFrame* indexFrame = nullptr;
  ReflowOutput baseSize(aReflowInput);
  ReflowOutput indexSize(aReflowInput);
  nsIFrame* childFrame = mFrames.FirstChild();
  while (childFrame) {
    // ask our children to compute their bounding metrics 
    ReflowOutput childDesiredSize(aReflowInput,
                                         aDesiredSize.mFlags
                                         | NS_REFLOW_CALC_BOUNDING_METRICS);
    WritingMode wm = childFrame->GetWritingMode();
    LogicalSize availSize = aReflowInput.ComputedSize(wm);
    availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
    ReflowInput childReflowInput(aPresContext, aReflowInput,
                                       childFrame, availSize);
    ReflowChild(childFrame, aPresContext,
                     childDesiredSize, childReflowInput, childStatus);
    //NS_ASSERTION(childStatus.IsComplete(), "bad status");
    if (0 == count) {
      // base 
      baseFrame = childFrame;
      baseSize = childDesiredSize;
      bmBase = childDesiredSize.mBoundingMetrics;
    }
    else if (1 == count) {
      // index
      indexFrame = childFrame;
      indexSize = childDesiredSize;
      bmIndex = childDesiredSize.mBoundingMetrics;
    }
    count++;
    childFrame = childFrame->GetNextSibling();
  }
  if (2 != count) {
    // report an error, encourage people to get their markups in order
    ReportChildCountError();
    ReflowError(drawTarget, aDesiredSize);
    aStatus.Reset();
    NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
    // Call DidReflow() for the child frames we successfully did reflow.
    DidReflowChildren(mFrames.FirstChild(), childFrame);
    return;
  }

  ////////////
  // Prepare the radical symbol and the overline bar

  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
  RefPtr<nsFontMetrics> fm =
    nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);

  nscoord ruleThickness, leading, psi;
  GetRadicalParameters(fm, StyleFont()->mMathDisplay ==
                       NS_MATHML_DISPLAYSTYLE_BLOCK,
                       ruleThickness, leading, psi);

  // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
  char16_t one = '1';
  nsBoundingMetrics bmOne =
    nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, drawTarget);
  if (bmOne.ascent > bmBase.ascent)
    psi += bmOne.ascent - bmBase.ascent;

  // make sure that the rule appears on on screen
  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
  if (ruleThickness < onePixel) {
    ruleThickness = onePixel;
  }

  // adjust clearance psi to get an exact number of pixels -- this
  // gives a nicer & uniform look on stacked radicals (bug 130282)
  nscoord delta = psi % onePixel;
  if (delta)
    psi += onePixel - delta; // round up

  // Stretch the radical symbol to the appropriate height if it is not big enough.
  nsBoundingMetrics contSize = bmBase;
  contSize.descent = bmBase.ascent + bmBase.descent + psi;
  contSize.ascent = ruleThickness;

  // height(radical) should be >= height(base) + psi + ruleThickness
  nsBoundingMetrics radicalSize;
  mSqrChar.Stretch(aPresContext, drawTarget,
                   fontSizeInflation,
                   NS_STRETCH_DIRECTION_VERTICAL, 
                   contSize, radicalSize,
                   NS_STRETCH_LARGER,
                   StyleVisibility()->mDirection);
  // radicalSize have changed at this point, and should match with
  // the bounding metrics of the char
  mSqrChar.GetBoundingMetrics(bmSqr);

  // Update the desired size for the container (like msqrt, index is not yet included)
  // the baseline will be that of the base.
  mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness;
  mBoundingMetrics.descent = 
    std::max(bmBase.descent,
           (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent));
  mBoundingMetrics.width = bmSqr.width + bmBase.width;
  mBoundingMetrics.leftBearing = bmSqr.leftBearing;
  mBoundingMetrics.rightBearing = bmSqr.width + 
    std::max(bmBase.width, bmBase.rightBearing); // take also care of the rule

  aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
  aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
    std::max(baseSize.Height() - baseSize.BlockStartAscent(),
             mBoundingMetrics.descent + ruleThickness);
  aDesiredSize.Width() = mBoundingMetrics.width;

  /////////////
  // Re-adjust the desired size to include the index.
  
  // the index is raised by some fraction of the height
  // of the radical, see \mroot macro in App. B, TexBook
  float raiseIndexPercent = 0.6f;
  gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
  if (mathFont) {
    raiseIndexPercent = mathFont->MathTable()->
      Constant(gfxMathTable::RadicalDegreeBottomRaisePercent);
  }
  nscoord raiseIndexDelta = NSToCoordRound(raiseIndexPercent *
                                           (bmSqr.ascent + bmSqr.descent));
  nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical 
    - (bmSqr.ascent + bmSqr.descent) // to bottom of radical
    + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index

  nscoord indexClearance = 0;
  if (mBoundingMetrics.ascent < indexRaisedAscent) {
    indexClearance = 
      indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index 
    mBoundingMetrics.ascent = indexRaisedAscent;
    nscoord descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
    aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
    aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + descent;
  }

  nscoord dxIndex, dxSqr;
  GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr);

  mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width;
  mBoundingMetrics.leftBearing = 
    std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
  mBoundingMetrics.rightBearing = dxSqr + bmSqr.width +
    std::max(bmBase.width, bmBase.rightBearing);

  aDesiredSize.Width() = mBoundingMetrics.width;
  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
  GatherAndStoreOverflow(&aDesiredSize);

  // place the index
  nscoord dx = dxIndex;
  nscoord dy = aDesiredSize.BlockStartAscent() -
    (indexRaisedAscent + indexSize.BlockStartAscent() - bmIndex.ascent);
  FinishReflowChild(indexFrame, aPresContext, indexSize, nullptr,
                    MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx),
                    dy, 0);

  // place the radical symbol and the radical bar
  dx = dxSqr;
  dy = indexClearance + leading; // leave a leading at the top
  mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx),
                          dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
  dx += bmSqr.width;
  mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(), bmBase.width, dx),
                   dy, bmBase.width, ruleThickness);

  // place the base
  dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
  FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr,
                    MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx),
                    dy, 0);

  mReference.x = 0;
  mReference.y = aDesiredSize.BlockStartAscent();

  aStatus.Reset();
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
}
Ejemplo n.º 11
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);
}
Ejemplo n.º 12
0
void
nsRubyFrame::ReflowSegment(nsPresContext* aPresContext,
                           const ReflowInput& aReflowInput,
                           nsRubyBaseContainerFrame* aBaseContainer,
                           nsReflowStatus& aStatus)
{
  WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode();
  LogicalSize availSize(lineWM, aReflowInput.AvailableISize(),
                        aReflowInput.AvailableBSize());
  WritingMode rubyWM = GetWritingMode();
  NS_ASSERTION(!rubyWM.IsOrthogonalTo(lineWM),
               "Ruby frame writing-mode shouldn't be orthogonal to its line");

  AutoRubyTextContainerArray textContainers(aBaseContainer);
  const uint32_t rtcCount = textContainers.Length();

  ReflowOutput baseMetrics(aReflowInput);
  bool pushedFrame;
  aReflowInput.mLineLayout->ReflowFrame(aBaseContainer, aStatus,
                                        &baseMetrics, pushedFrame);

  if (aStatus.IsInlineBreakBefore()) {
    if (aBaseContainer != mFrames.FirstChild()) {
      // Some segments may have been reflowed before, hence it is not
      // a break-before for the ruby container.
      aStatus.Reset();
      aStatus.SetInlineLineBreakAfter();
      aStatus.SetIncomplete();
      PushChildrenToOverflow(aBaseContainer, aBaseContainer->GetPrevSibling());
      aReflowInput.mLineLayout->SetDirtyNextLine();
    }
    // This base container is not placed at all, we can skip all
    // text containers paired with it.
    return;
  }
  if (aStatus.IsIncomplete()) {
    // It always promise that if the status is incomplete, there is a
    // break occurs. Break before has been processed above. However,
    // it is possible that break after happens with the frame reflow
    // completed. It happens if there is a force break at the end.
    MOZ_ASSERT(aStatus.IsInlineBreakAfter());
    // Find the previous sibling which we will
    // insert new continuations after.
    nsIFrame* lastChild;
    if (rtcCount > 0) {
      lastChild = textContainers.LastElement();
    } else {
      lastChild = aBaseContainer;
    }

    // Create continuations for the base container
    nsIFrame* newBaseContainer = CreateNextInFlow(aBaseContainer);
    // newBaseContainer is null if there are existing next-in-flows.
    // We only need to move and push if there were not.
    if (newBaseContainer) {
      // Move the new frame after all the text containers
      mFrames.RemoveFrame(newBaseContainer);
      mFrames.InsertFrame(nullptr, lastChild, newBaseContainer);

      // Create continuations for text containers
      nsIFrame* newLastChild = newBaseContainer;
      for (uint32_t i = 0; i < rtcCount; i++) {
        nsIFrame* newTextContainer = CreateNextInFlow(textContainers[i]);
        MOZ_ASSERT(newTextContainer, "Next-in-flow of rtc should not exist "
                   "if the corresponding rbc does not");
        mFrames.RemoveFrame(newTextContainer);
        mFrames.InsertFrame(nullptr, newLastChild, newTextContainer);
        newLastChild = newTextContainer;
      }
    }
    if (lastChild != mFrames.LastChild()) {
      // Always push the next frame after the last child in this segment.
      // It is possible that we pulled it back before our next-in-flow
      // drain our overflow.
      PushChildrenToOverflow(lastChild->GetNextSibling(), lastChild);
      aReflowInput.mLineLayout->SetDirtyNextLine();
    }
  } else {
    // If the ruby base container is reflowed completely, the line
    // layout will remove the next-in-flows of that frame. But the
    // line layout is not aware of the ruby text containers, hence
    // it is necessary to remove them here.
    for (uint32_t i = 0; i < rtcCount; i++) {
      nsIFrame* nextRTC = textContainers[i]->GetNextInFlow();
      if (nextRTC) {
        nextRTC->GetParent()->DeleteNextInFlowChild(nextRTC, true);
      }
    }
  }

  nscoord segmentISize = baseMetrics.ISize(lineWM);
  const nsSize dummyContainerSize;
  LogicalRect baseRect =
    aBaseContainer->GetLogicalRect(lineWM, dummyContainerSize);
  // We need to position our rtc frames on one side or the other of the
  // base container's rect, using a coordinate space that's relative to
  // the ruby frame. Right now, the base container's rect's block-axis
  // position is relative to the block container frame containing the
  // lines, so we use 0 instead. (i.e. we assume that the base container
  // is adjacent to the ruby frame's block-start edge.)
  // XXX We may need to add border/padding here. See bug 1055667.
  baseRect.BStart(lineWM) = 0;
  // The rect for offsets of text containers.
  LogicalRect offsetRect = baseRect;
  RubyBlockLeadings descLeadings = aBaseContainer->GetDescendantLeadings();
  offsetRect.BStart(lineWM) -= descLeadings.mStart;
  offsetRect.BSize(lineWM) += descLeadings.mStart + descLeadings.mEnd;
  for (uint32_t i = 0; i < rtcCount; i++) {
    nsRubyTextContainerFrame* textContainer = textContainers[i];
    WritingMode rtcWM = textContainer->GetWritingMode();
    nsReflowStatus textReflowStatus;
    ReflowOutput textMetrics(aReflowInput);
    ReflowInput textReflowInput(aPresContext, aReflowInput, textContainer,
                                      availSize.ConvertTo(rtcWM, lineWM));
    textContainer->Reflow(aPresContext, textMetrics,
                          textReflowInput, textReflowStatus);
    // Ruby text containers always return complete reflow status even when
    // they have continuations, because the breaking has already been
    // handled when reflowing the base containers.
    NS_ASSERTION(textReflowStatus.IsEmpty(),
                 "Ruby text container must not break itself inside");
    // The metrics is initialized with reflow state of this ruby frame,
    // hence the writing-mode is tied to rubyWM instead of rtcWM.
    LogicalSize size = textMetrics.Size(rubyWM).ConvertTo(lineWM, rubyWM);
    textContainer->SetSize(lineWM, size);

    nscoord reservedISize = RubyUtils::GetReservedISize(textContainer);
    segmentISize = std::max(segmentISize, size.ISize(lineWM) + reservedISize);

    uint8_t rubyPosition = textContainer->StyleText()->mRubyPosition;
    MOZ_ASSERT(rubyPosition == NS_STYLE_RUBY_POSITION_OVER ||
               rubyPosition == NS_STYLE_RUBY_POSITION_UNDER);
    Maybe<LogicalSide> side;
    if (rubyPosition == NS_STYLE_RUBY_POSITION_OVER) {
      side.emplace(lineWM.LogicalSideForLineRelativeDir(eLineRelativeDirOver));
    } else if (rubyPosition == NS_STYLE_RUBY_POSITION_UNDER) {
      side.emplace(lineWM.LogicalSideForLineRelativeDir(eLineRelativeDirUnder));
    } else {
      // XXX inter-character support in bug 1055672
      MOZ_ASSERT_UNREACHABLE("Unsupported ruby-position");
    }

    LogicalPoint position(lineWM);
    if (side.isSome()) {
      if (nsLayoutUtils::IsInterCharacterRubyEnabled() &&
          rtcWM.IsVerticalRL() &&
          lineWM.GetInlineDir() == WritingMode::eInlineLTR) {
        // Inter-character ruby annotations are only supported for vertical-rl
        // in ltr horizontal writing. Fall back to non-inter-character behavior
        // otherwise.
        LogicalPoint offset(lineWM, offsetRect.ISize(lineWM),
          offsetRect.BSize(lineWM) > size.BSize(lineWM) ?
          (offsetRect.BSize(lineWM) - size.BSize(lineWM)) / 2 : 0);
        position = offsetRect.Origin(lineWM) + offset;
        aReflowInput.mLineLayout->AdvanceICoord(size.ISize(lineWM));
      } else if (side.value() == eLogicalSideBStart) {
        offsetRect.BStart(lineWM) -= size.BSize(lineWM);
        offsetRect.BSize(lineWM) += size.BSize(lineWM);
        position = offsetRect.Origin(lineWM);
      } else if (side.value() == eLogicalSideBEnd) {
        position = offsetRect.Origin(lineWM) +
          LogicalPoint(lineWM, 0, offsetRect.BSize(lineWM));
        offsetRect.BSize(lineWM) += size.BSize(lineWM);
      } else {
        MOZ_ASSERT_UNREACHABLE("???");
      }
    }
    // Using a dummy container-size here, so child positioning may not be
    // correct. We will fix it in nsLineLayout after the whole line is
    // reflowed.
    FinishReflowChild(textContainer, aPresContext, textMetrics,
                      &textReflowInput, lineWM, position, dummyContainerSize, 0);
  }
  MOZ_ASSERT(baseRect.ISize(lineWM) == offsetRect.ISize(lineWM),
             "Annotations should only be placed on the block directions");

  nscoord deltaISize = segmentISize - baseMetrics.ISize(lineWM);
  if (deltaISize <= 0) {
    RubyUtils::ClearReservedISize(aBaseContainer);
  } else {
    RubyUtils::SetReservedISize(aBaseContainer, deltaISize);
    aReflowInput.mLineLayout->AdvanceICoord(deltaISize);
  }

  // Set block leadings of the base container
  nscoord startLeading = baseRect.BStart(lineWM) - offsetRect.BStart(lineWM);
  nscoord endLeading = offsetRect.BEnd(lineWM) - baseRect.BEnd(lineWM);
  // XXX When bug 765861 gets fixed, this warning should be upgraded.
  NS_WARNING_ASSERTION(startLeading >= 0 && endLeading >= 0,
                       "Leadings should be non-negative (because adding "
                       "ruby annotation can only increase the size)");
  mLeadings.Update(startLeading, endLeading);
}
Ejemplo n.º 13
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);
  MOZ_ASSERT(aReflowStatus.IsEmpty(), "Caller should pass a fresh reflow status!");

  // 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, rs.mStyleDisplay);
    kid->DidReflow(aPresContext, nullptr);

    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, aReflowInput.mStyleDisplay);
  } 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 (!aReflowStatus.IsInlineBreakBefore()) {
    // Create a continuation or remove existing continuations based on
    // the reflow completion status.
    if (aReflowStatus.IsComplete()) {
      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);
}