예제 #1
0
void
nsTextControlFrame::ReflowTextControlChild(nsIFrame*                aKid,
                                           nsPresContext*           aPresContext,
                                           const ReflowInput& aReflowInput,
                                           nsReflowStatus&          aStatus,
                                           ReflowOutput& aParentDesiredSize)
{
  // compute available size and frame offsets for child
  WritingMode wm = aKid->GetWritingMode();
  LogicalSize availSize = aReflowInput.ComputedSizeWithPadding(wm);
  availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;

  ReflowInput kidReflowInput(aPresContext, aReflowInput, 
                                   aKid, availSize, nullptr,
                                   ReflowInput::CALLER_WILL_INIT);
  // Override padding with our computed padding in case we got it from theming or percentage
  kidReflowInput.Init(aPresContext, nullptr, nullptr, &aReflowInput.ComputedPhysicalPadding());

  // Set computed width and computed height for the child
  kidReflowInput.SetComputedWidth(aReflowInput.ComputedWidth());
  kidReflowInput.SetComputedHeight(aReflowInput.ComputedHeight());

  // Offset the frame by the size of the parent's border
  nscoord xOffset = aReflowInput.ComputedPhysicalBorderPadding().left -
                    aReflowInput.ComputedPhysicalPadding().left;
  nscoord yOffset = aReflowInput.ComputedPhysicalBorderPadding().top -
                    aReflowInput.ComputedPhysicalPadding().top;

  // reflow the child
  ReflowOutput desiredSize(aReflowInput);
  ReflowChild(aKid, aPresContext, desiredSize, kidReflowInput, 
              xOffset, yOffset, 0, aStatus);

  // place the child
  FinishReflowChild(aKid, aPresContext, desiredSize,
                    &kidReflowInput, xOffset, yOffset, 0);

  // consider the overflow
  aParentDesiredSize.mOverflowAreas.UnionWith(desiredSize.mOverflowAreas);
}
void
nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame*                aDelegatingFrame,
                                               nsPresContext*           aPresContext,
                                               const ReflowInput& aReflowInput,
                                               const nsRect&            aContainingBlock,
                                               AbsPosReflowFlags        aFlags,
                                               nsIFrame*                aKidFrame,
                                               nsReflowStatus&          aStatus,
                                               nsOverflowAreas*         aOverflowAreas)
{
#ifdef DEBUG
  if (nsBlockFrame::gNoisyReflow) {
    nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent);
    printf("abs pos ");
    nsAutoString name;
    aKidFrame->GetFrameName(name);
    printf("%s ", NS_LossyConvertUTF16toASCII(name).get());

    char width[16];
    char height[16];
    PrettyUC(aReflowInput.AvailableWidth(), width, 16);
    PrettyUC(aReflowInput.AvailableHeight(), height, 16);
    printf(" a=%s,%s ", width, height);
    PrettyUC(aReflowInput.ComputedWidth(), width, 16);
    PrettyUC(aReflowInput.ComputedHeight(), height, 16);
    printf("c=%s,%s \n", width, height);
  }
  AutoNoisyIndenter indent(nsBlockFrame::gNoisy);
#endif // DEBUG

  WritingMode wm = aKidFrame->GetWritingMode();
  LogicalSize logicalCBSize(wm, aContainingBlock.Size());
  nscoord availISize = logicalCBSize.ISize(wm);
  if (availISize == -1) {
    NS_ASSERTION(aReflowInput.ComputedSize(wm).ISize(wm) !=
                   NS_UNCONSTRAINEDSIZE,
                 "Must have a useful inline-size _somewhere_");
    availISize =
      aReflowInput.ComputedSizeWithPadding(wm).ISize(wm);
  }

  uint32_t rsFlags = 0;
  if (aFlags & AbsPosReflowFlags::eIsGridContainerCB) {
    // When a grid container generates the abs.pos. CB for a *child* then
    // the static-position is the CB origin (i.e. of the grid area rect).
    // https://drafts.csswg.org/css-grid/#static-position
    nsIFrame* placeholder =
      aPresContext->PresShell()->GetPlaceholderFrameFor(aKidFrame);
    if (placeholder && placeholder->GetParent() == aDelegatingFrame) {
      rsFlags |= ReflowInput::STATIC_POS_IS_CB_ORIGIN;
    }
  }
  ReflowInput kidReflowInput(aPresContext, aReflowInput, aKidFrame,
                                   LogicalSize(wm, availISize,
                                               NS_UNCONSTRAINEDSIZE),
                                   &logicalCBSize, rsFlags);

  // Get the border values
  WritingMode outerWM = aReflowInput.GetWritingMode();
  const LogicalMargin border(outerWM,
                             aReflowInput.mStyleBorder->GetComputedBorder());
  LogicalMargin margin =
    kidReflowInput.ComputedLogicalMargin().ConvertTo(outerWM, wm);

  // If we're doing CSS Box Alignment in either axis, that will apply the
  // margin for us in that axis (since the thing that's aligned is the margin
  // box).  So, we clear out the margin here to avoid applying it twice.
  if (kidReflowInput.mFlags.mIOffsetsNeedCSSAlign) {
    margin.IStart(outerWM) = margin.IEnd(outerWM) = 0;
  }
  if (kidReflowInput.mFlags.mBOffsetsNeedCSSAlign) {
    margin.BStart(outerWM) = margin.BEnd(outerWM) = 0;
  }

  bool constrainBSize = (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE)
    && (aFlags & AbsPosReflowFlags::eConstrainHeight)
       // Don't split if told not to (e.g. for fixed frames)
    && (aDelegatingFrame->GetType() != nsGkAtoms::inlineFrame)
       //XXX we don't handle splitting frames for inline absolute containing blocks yet
    && (aKidFrame->GetLogicalRect(aContainingBlock.Size()).BStart(wm) <=
        aReflowInput.AvailableBSize());
       // Don't split things below the fold. (Ideally we shouldn't *have*
       // anything totally below the fold, but we can't position frames
       // across next-in-flow breaks yet.
  if (constrainBSize) {
    kidReflowInput.AvailableBSize() =
      aReflowInput.AvailableBSize() - border.ConvertTo(wm, outerWM).BStart(wm) -
      kidReflowInput.ComputedLogicalMargin().BStart(wm);
    if (NS_AUTOOFFSET != kidReflowInput.ComputedLogicalOffsets().BStart(wm)) {
      kidReflowInput.AvailableBSize() -=
        kidReflowInput.ComputedLogicalOffsets().BStart(wm);
    }
  }

  // Do the reflow
  ReflowOutput kidDesiredSize(kidReflowInput);
  aKidFrame->Reflow(aPresContext, kidDesiredSize, kidReflowInput, aStatus);

  const LogicalSize kidSize = kidDesiredSize.Size(wm).ConvertTo(outerWM, wm);

  LogicalMargin offsets =
    kidReflowInput.ComputedLogicalOffsets().ConvertTo(outerWM, wm);

  // If we're solving for start in either inline or block direction,
  // then compute it now that we know the dimensions.
  ResolveSizeDependentOffsets(aPresContext, kidReflowInput, kidSize, margin,
                              &offsets, &logicalCBSize);

  // Position the child relative to our padding edge
  LogicalRect rect(outerWM,
                   border.IStart(outerWM) + offsets.IStart(outerWM) +
                     margin.IStart(outerWM),
                   border.BStart(outerWM) + offsets.BStart(outerWM) +
                     margin.BStart(outerWM),
                   kidSize.ISize(outerWM), kidSize.BSize(outerWM));
  nsRect r =
    rect.GetPhysicalRect(outerWM, logicalCBSize.GetPhysicalSize(wm) +
                         border.Size(outerWM).GetPhysicalSize(outerWM));

  // Offset the frame rect by the given origin of the absolute containing block.
  // If the frame is auto-positioned on both sides of an axis, it will be
  // positioned based on its containing block and we don't need to offset
  // (unless the caller demands it (the STATIC_POS_IS_CB_ORIGIN case)).
  if (aContainingBlock.TopLeft() != nsPoint(0, 0)) {
    const nsStyleSides& offsets = kidReflowInput.mStylePosition->mOffset;
    if (!(offsets.GetLeftUnit() == eStyleUnit_Auto &&
          offsets.GetRightUnit() == eStyleUnit_Auto) ||
        (rsFlags & ReflowInput::STATIC_POS_IS_CB_ORIGIN)) {
      r.x += aContainingBlock.x;
    }
    if (!(offsets.GetTopUnit() == eStyleUnit_Auto &&
          offsets.GetBottomUnit() == eStyleUnit_Auto) ||
        (rsFlags & ReflowInput::STATIC_POS_IS_CB_ORIGIN)) {
      r.y += aContainingBlock.y;
    }
  }

  aKidFrame->SetRect(r);

  nsView* view = aKidFrame->GetView();
  if (view) {
    // Size and position the view and set its opacity, visibility, content
    // transparency, and clip
    nsContainerFrame::SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
                                               kidDesiredSize.VisualOverflow());
  } else {
    nsContainerFrame::PositionChildViews(aKidFrame);
  }

  aKidFrame->DidReflow(aPresContext, &kidReflowInput,
                       nsDidReflowStatus::FINISHED);

#ifdef DEBUG
  if (nsBlockFrame::gNoisyReflow) {
    nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent - 1);
    printf("abs pos ");
    nsAutoString name;
    aKidFrame->GetFrameName(name);
    printf("%s ", NS_LossyConvertUTF16toASCII(name).get());
    printf("%p rect=%d,%d,%d,%d\n", static_cast<void*>(aKidFrame),
           r.x, r.y, r.width, r.height);
  }
#endif

  if (aOverflowAreas) {
    aOverflowAreas->UnionWith(kidDesiredSize.mOverflowAreas + r.TopLeft());
  }
}
예제 #3
0
void
nsFieldSetFrame::Reflow(nsPresContext*           aPresContext,
                        ReflowOutput&     aDesiredSize,
                        const ReflowInput& aReflowInput,
                        nsReflowStatus&          aStatus)
{
  MarkInReflow();
  DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame");
  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);

  NS_PRECONDITION(aReflowInput.ComputedISize() != NS_INTRINSICSIZE,
                  "Should have a precomputed inline-size!");

  // Initialize OUT parameter
  aStatus = NS_FRAME_COMPLETE;

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

  //------------ Handle Incremental Reflow -----------------
  bool reflowInner;
  bool reflowLegend;
  nsIFrame* legend = GetLegend();
  nsIFrame* inner = GetInner();
  if (aReflowInput.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 wm = GetWritingMode();
  WritingMode innerWM = inner ? inner->GetWritingMode() : wm;
  WritingMode legendWM = legend ? legend->GetWritingMode() : wm;
  LogicalSize innerAvailSize = aReflowInput.ComputedSizeWithPadding(innerWM);
  LogicalSize legendAvailSize = aReflowInput.ComputedSizeWithPadding(legendWM);
  innerAvailSize.BSize(innerWM) = legendAvailSize.BSize(legendWM) =
    NS_UNCONSTRAINEDSIZE;
  NS_ASSERTION(!inner ||
      nsLayoutUtils::IntrinsicForContainer(aReflowInput.mRenderingContext,
                                           inner,
                                           nsLayoutUtils::MIN_ISIZE) <=
               innerAvailSize.ISize(innerWM),
               "Bogus availSize.ISize; should be bigger");
  NS_ASSERTION(!legend ||
      nsLayoutUtils::IntrinsicForContainer(aReflowInput.mRenderingContext,
                                           legend,
                                           nsLayoutUtils::MIN_ISIZE) <=
               legendAvailSize.ISize(legendWM),
               "Bogus availSize.ISize; should be bigger");

  // get our border and padding
  LogicalMargin border = aReflowInput.ComputedLogicalBorderPadding() -
                         aReflowInput.ComputedLogicalPadding();

  // Figure out how big the legend is if there is one.
  // get the legend's margin
  LogicalMargin legendMargin(wm);
  // reflow the legend only if needed
  Maybe<ReflowInput> legendReflowInput;
  if (legend) {
    legendReflowInput.emplace(aPresContext, aReflowInput, legend,
                                legendAvailSize);
  }
  if (reflowLegend) {
    ReflowOutput legendDesiredSize(aReflowInput);

    // We'll move the legend to its proper place later, so the position
    // and containerSize passed here are unimportant.
    const nsSize dummyContainerSize;
    ReflowChild(legend, aPresContext, legendDesiredSize, *legendReflowInput,
                wm, LogicalPoint(wm), dummyContainerSize,
                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->GetLogicalUsedMargin(wm);
    mLegendRect =
      LogicalRect(wm, 0, 0,
                  legendDesiredSize.ISize(wm) + legendMargin.IStartEnd(wm),
                  legendDesiredSize.BSize(wm) + legendMargin.BStartEnd(wm));
    nscoord oldSpace = mLegendSpace;
    mLegendSpace = 0;
    if (mLegendRect.BSize(wm) > border.BStart(wm)) {
      // center the border on the legend
      mLegendSpace = mLegendRect.BSize(wm) - border.BStart(wm);
    } else {
      mLegendRect.BStart(wm) =
        (border.BStart(wm) - mLegendRect.BSize(wm)) / 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,
                      legendReflowInput.ptr(), wm, LogicalPoint(wm),
                      dummyContainerSize, 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->GetLogicalUsedMargin(wm);
  }

  // This containerSize is incomplete as yet: it does not include the size
  // of the |inner| frame itself.
  nsSize containerSize = (LogicalSize(wm, 0, mLegendSpace) +
                          border.Size(wm)).GetPhysicalSize(wm);
  // reflow the content frame only if needed
  if (reflowInner) {
    ReflowInput kidReflowInput(aPresContext, aReflowInput, inner,
                                     innerAvailSize, nullptr,
                                     ReflowInput::CALLER_WILL_INIT);
    // Override computed padding, in case it's percentage padding
    kidReflowInput.Init(aPresContext, nullptr, nullptr,
                        &aReflowInput.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 (aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) {
      kidReflowInput.SetComputedBSize(
         std::max(0, aReflowInput.ComputedBSize() - mLegendSpace));
    }

    if (aReflowInput.ComputedMinBSize() > 0) {
      kidReflowInput.ComputedMinBSize() =
        std::max(0, aReflowInput.ComputedMinBSize() - mLegendSpace);
    }

    if (aReflowInput.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE) {
      kidReflowInput.ComputedMaxBSize() =
        std::max(0, aReflowInput.ComputedMaxBSize() - mLegendSpace);
    }

    ReflowOutput kidDesiredSize(kidReflowInput,
                                       aDesiredSize.mFlags);
    // Reflow the frame
    NS_ASSERTION(kidReflowInput.ComputedPhysicalMargin() == nsMargin(0,0,0,0),
                 "Margins on anonymous fieldset child not supported!");
    LogicalPoint pt(wm, border.IStart(wm), border.BStart(wm) + mLegendSpace);

    // We don't know the correct containerSize until we have reflowed |inner|,
    // so we use a dummy value for now; FinishReflowChild will fix the position
    // if necessary.
    const nsSize dummyContainerSize;
    ReflowChild(inner, aPresContext, kidDesiredSize, kidReflowInput,
                wm, pt, dummyContainerSize, 0, aStatus);

    // Update containerSize to account for size of the inner frame, so that
    // FinishReflowChild can position it correctly.
    containerSize += kidDesiredSize.PhysicalSize();
    FinishReflowChild(inner, aPresContext, kidDesiredSize,
                      &kidReflowInput, wm, pt, containerSize, 0);
    NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus);
  } else if (inner) {
    // |inner| didn't need to be reflowed but we do need to include its size
    // in containerSize.
    containerSize += inner->GetSize();
  }

  LogicalRect contentRect(wm);
  if (inner) {
    // We don't support margins on inner, so our content rect is just the
    // inner's border-box. (We don't really care about container size at this
    // point, as we'll figure out the actual positioning later.)
    contentRect = inner->GetLogicalRect(wm, containerSize);
  }

  // Our content rect must fill up the available width
  LogicalSize availSize = aReflowInput.ComputedSizeWithPadding(wm);
  if (availSize.ISize(wm) > contentRect.ISize(wm)) {
    contentRect.ISize(wm) = innerAvailSize.ISize(wm);
  }

  if (legend) {
    // The legend is positioned inline-wards within the inner's content rect
    // (so that padding on the fieldset affects the legend position).
    LogicalRect innerContentRect = contentRect;
    innerContentRect.Deflate(wm, aReflowInput.ComputedLogicalPadding());
    // If the inner content rect is larger than the legend, we can align the
    // legend.
    if (innerContentRect.ISize(wm) > mLegendRect.ISize(wm)) {
      // NOTE legend @align values are: left/right/center/top/bottom.
      // GetLogicalAlign converts left/right to start/end for the given WM.
      // @see HTMLLegendElement::ParseAttribute, nsLegendFrame::GetLogicalAlign
      int32_t align = static_cast<nsLegendFrame*>
        (legend->GetContentInsertionFrame())->GetLogicalAlign(wm);
      switch (align) {
        case NS_STYLE_TEXT_ALIGN_END:
          mLegendRect.IStart(wm) =
            innerContentRect.IEnd(wm) - mLegendRect.ISize(wm);
          break;
        case NS_STYLE_TEXT_ALIGN_CENTER:
          // Note: rounding removed; there doesn't seem to be any need
          mLegendRect.IStart(wm) = innerContentRect.IStart(wm) +
            (innerContentRect.ISize(wm) - mLegendRect.ISize(wm)) / 2;
          break;
        case NS_STYLE_TEXT_ALIGN_START:
        case NS_STYLE_VERTICAL_ALIGN_TOP:
        case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
          mLegendRect.IStart(wm) = innerContentRect.IStart(wm);
          break;
        default:
          MOZ_ASSERT_UNREACHABLE("unexpected GetLogicalAlign value");
      }
    } else {
      // otherwise make place for the legend
      mLegendRect.IStart(wm) = innerContentRect.IStart(wm);
      innerContentRect.ISize(wm) = mLegendRect.ISize(wm);
      contentRect.ISize(wm) = mLegendRect.ISize(wm) +
        aReflowInput.ComputedLogicalPadding().IStartEnd(wm);
    }

    // place the legend
    LogicalRect actualLegendRect = mLegendRect;
    actualLegendRect.Deflate(wm, legendMargin);
    LogicalPoint actualLegendPos(actualLegendRect.Origin(wm));

    // Note that legend's writing mode may be different from the fieldset's,
    // so we need to convert offsets before applying them to it (bug 1134534).
    LogicalMargin offsets =
      legendReflowInput->ComputedLogicalOffsets().
        ConvertTo(wm, legendReflowInput->GetWritingMode());
    ReflowInput::ApplyRelativePositioning(legend, wm, offsets,
                                                &actualLegendPos,
                                                containerSize);

    legend->SetPosition(wm, actualLegendPos, containerSize);
    nsContainerFrame::PositionFrameView(legend);
    nsContainerFrame::PositionChildViews(legend);
  }

  // Return our size and our result.
  LogicalSize finalSize(wm, contentRect.ISize(wm) + border.IStartEnd(wm),
                        mLegendSpace + border.BStartEnd(wm) +
                        (inner ? inner->BSize(wm) : 0));
  aDesiredSize.SetSize(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, aReflowInput, aStatus);

  InvalidateFrame();

  NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
}