Example #1
0
void
nsFieldSetFrame::Reflow(nsPresContext*           aPresContext,
                        nsHTMLReflowMetrics&     aDesiredSize,
                        const nsHTMLReflowState& aReflowState,
                        nsReflowStatus&          aStatus)
{
  DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);

  NS_PRECONDITION(aReflowState.ComputedWidth() != NS_INTRINSICSIZE,
                  "Should have a precomputed width!");      
  
  // Initialize OUT parameter
  aStatus = NS_FRAME_COMPLETE;

  nsOverflowAreas ocBounds;
  nsReflowStatus ocStatus = NS_FRAME_COMPLETE;
  if (GetPrevInFlow()) {
    ReflowOverflowContainerChildren(aPresContext, aReflowState, ocBounds, 0,
                                    ocStatus);
  }

  //------------ Handle Incremental Reflow -----------------
  bool reflowInner;
  bool reflowLegend;
  nsIFrame* legend = GetLegend();
  nsIFrame* inner = GetInner();
  if (aReflowState.ShouldReflowAllKids()) {
    reflowInner = inner != nullptr;
    reflowLegend = legend != nullptr;
  } else {
    reflowInner = inner && NS_SUBTREE_DIRTY(inner);
    reflowLegend = legend && NS_SUBTREE_DIRTY(legend);
  }

  // We don't allow fieldsets to break vertically. If we did, we'd
  // need logic here to push and pull overflow frames.
  // Since we're not applying our padding in this frame, we need to add it here
  // to compute the available width for our children.
  WritingMode innerWM = inner ? inner->GetWritingMode() : GetWritingMode();
  WritingMode legendWM = legend ? legend->GetWritingMode() : GetWritingMode();
  LogicalSize innerAvailSize = aReflowState.ComputedSizeWithPadding(innerWM);
  LogicalSize legendAvailSize = aReflowState.ComputedSizeWithPadding(legendWM);
  innerAvailSize.BSize(innerWM) = legendAvailSize.BSize(legendWM) =
    NS_UNCONSTRAINEDSIZE;
  NS_ASSERTION(!inner ||
      nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext,
                                           inner,
                                           nsLayoutUtils::MIN_ISIZE) <=
               innerAvailSize.ISize(innerWM),
               "Bogus availSize.ISize; should be bigger");
  NS_ASSERTION(!legend ||
      nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext,
                                           legend,
                                           nsLayoutUtils::MIN_ISIZE) <=
               legendAvailSize.ISize(legendWM),
               "Bogus availSize.ISize; should be bigger");

  // get our border and padding
  nsMargin border = aReflowState.ComputedPhysicalBorderPadding() - aReflowState.ComputedPhysicalPadding();

  // Figure out how big the legend is if there is one. 
  // get the legend's margin
  nsMargin legendMargin(0,0,0,0);
  // reflow the legend only if needed
  Maybe<nsHTMLReflowState> legendReflowState;
  if (legend) {
    legendReflowState.emplace(aPresContext, aReflowState, legend,
                                legendAvailSize);
  }
  if (reflowLegend) {
    nsHTMLReflowMetrics legendDesiredSize(aReflowState);

    ReflowChild(legend, aPresContext, legendDesiredSize, *legendReflowState,
                0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
#ifdef NOISY_REFLOW
    printf("  returned (%d, %d)\n",
           legendDesiredSize.Width(), legendDesiredSize.Height());
#endif
    // figure out the legend's rectangle
    legendMargin = legend->GetUsedMargin();
    mLegendRect.width  = legendDesiredSize.Width() + legendMargin.left + legendMargin.right;
    mLegendRect.height = legendDesiredSize.Height() + legendMargin.top + legendMargin.bottom;
    mLegendRect.x = 0;
    mLegendRect.y = 0;

    nscoord oldSpace = mLegendSpace;
    mLegendSpace = 0;
    if (mLegendRect.height > border.top) {
      // center the border on the legend
      mLegendSpace = mLegendRect.height - border.top;
    } else {
      mLegendRect.y = (border.top - mLegendRect.height)/2;
    }

    // if the legend space changes then we need to reflow the 
    // content area as well.
    if (mLegendSpace != oldSpace && inner) {
      reflowInner = true;
    }

    FinishReflowChild(legend, aPresContext, legendDesiredSize,
                      legendReflowState.ptr(), 0, 0, NS_FRAME_NO_MOVE_FRAME);    
  } else if (!legend) {
    mLegendRect.SetEmpty();
    mLegendSpace = 0;
  } else {
    // mLegendSpace and mLegendRect haven't changed, but we need
    // the used margin when placing the legend.
    legendMargin = legend->GetUsedMargin();
  }

  // reflow the content frame only if needed
  if (reflowInner) {
    nsHTMLReflowState kidReflowState(aPresContext, aReflowState, inner,
                                     innerAvailSize, -1, -1,
                                     nsHTMLReflowState::CALLER_WILL_INIT);
    // Override computed padding, in case it's percentage padding
    kidReflowState.Init(aPresContext, -1, -1, nullptr,
                        &aReflowState.ComputedPhysicalPadding());
    // Our child is "height:100%" but we actually want its height to be reduced
    // by the amount of content-height the legend is eating up, unless our
    // height is unconstrained (in which case the child's will be too).
    if (aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE) {
      kidReflowState.SetComputedHeight(
         std::max(0, aReflowState.ComputedHeight() - mLegendSpace));
    }

    if (aReflowState.ComputedMinHeight() > 0) {
      kidReflowState.ComputedMinHeight() =
        std::max(0, aReflowState.ComputedMinHeight() - mLegendSpace);
    }

    if (aReflowState.ComputedMaxHeight() != NS_UNCONSTRAINEDSIZE) {
      kidReflowState.ComputedMaxHeight() =
        std::max(0, aReflowState.ComputedMaxHeight() - mLegendSpace);
    }

    nsHTMLReflowMetrics kidDesiredSize(kidReflowState,
                                       aDesiredSize.mFlags);
    // Reflow the frame
    NS_ASSERTION(kidReflowState.ComputedPhysicalMargin() == nsMargin(0,0,0,0),
                 "Margins on anonymous fieldset child not supported!");
    nsPoint pt(border.left, border.top + mLegendSpace);
    ReflowChild(inner, aPresContext, kidDesiredSize, kidReflowState,
                pt.x, pt.y, 0, aStatus);

    FinishReflowChild(inner, aPresContext, kidDesiredSize,
                      &kidReflowState, pt.x, pt.y, 0);
    NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus);
  }

  LogicalRect contentRect(innerWM);
  if (inner) {
    // We don't support margins on inner, so our content rect is just the
    // inner's border-box.
    contentRect = inner->GetLogicalRect(aReflowState.ComputedWidth());
  }

  // Our content rect must fill up the available width
  if (innerAvailSize.ISize(innerWM) > contentRect.ISize(innerWM)) {
    contentRect.ISize(innerWM) = innerAvailSize.ISize(innerWM);
  }

  //XXX temporary!
  nsRect physicalContentRect =
    contentRect.GetPhysicalRect(innerWM, aReflowState.ComputedWidth());
  if (legend) {
    // the legend is postioned horizontally within the inner's content rect
    // (so that padding on the fieldset affects the legend position).
    nsRect innerContentRect = physicalContentRect;
    innerContentRect.Deflate(aReflowState.ComputedPhysicalPadding());
    // if the inner content rect is larger than the legend, we can align the legend
    if (innerContentRect.width > mLegendRect.width) {
      int32_t align = static_cast<nsLegendFrame*>
        (legend->GetContentInsertionFrame())->GetAlign();

      switch (align) {
        case NS_STYLE_TEXT_ALIGN_RIGHT:
          mLegendRect.x = innerContentRect.XMost() - mLegendRect.width;
          break;
        case NS_STYLE_TEXT_ALIGN_CENTER:
          // Note: rounding removed; there doesn't seem to be any need
          mLegendRect.x = innerContentRect.width / 2 - mLegendRect.width / 2 + innerContentRect.x;
          break;
        default:
          mLegendRect.x = innerContentRect.x;
          break;
      }
    } else {
      // otherwise make place for the legend
      mLegendRect.x = innerContentRect.x;
      innerContentRect.width = mLegendRect.width;
      physicalContentRect.width = mLegendRect.width +
        aReflowState.ComputedPhysicalPadding().LeftRight();
    }

    // place the legend
    nsRect actualLegendRect(mLegendRect);
    actualLegendRect.Deflate(legendMargin);
    nsPoint actualLegendPos(actualLegendRect.TopLeft());
    legendReflowState->ApplyRelativePositioning(&actualLegendPos);
    legend->SetPosition(actualLegendPos);
    nsContainerFrame::PositionFrameView(legend);
    nsContainerFrame::PositionChildViews(legend);
  }

  // Return our size and our result.
  WritingMode wm = aReflowState.GetWritingMode();
  nsSize finalSize(physicalContentRect.width + border.LeftRight(),
                   mLegendSpace + border.TopBottom() +
                   (inner ? inner->GetRect().height : 0));
  aDesiredSize.SetSize(wm, LogicalSize(wm, finalSize));
  aDesiredSize.SetOverflowAreasToDesiredBounds();
  if (legend)
    ConsiderChildOverflow(aDesiredSize.mOverflowAreas, legend);
  if (inner)
    ConsiderChildOverflow(aDesiredSize.mOverflowAreas, inner);

  // Merge overflow container bounds and status.
  aDesiredSize.mOverflowAreas.UnionWith(ocBounds);
  NS_MergeReflowStatusInto(&aStatus, ocStatus);

  FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus);

  InvalidateFrame();

  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
}