forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			676 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			676 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "nsFieldSetFrame.h"
 | |
| 
 | |
| #include <algorithm>
 | |
| #include "gfxContext.h"
 | |
| #include "mozilla/gfx/2D.h"
 | |
| #include "mozilla/Likely.h"
 | |
| #include "mozilla/Maybe.h"
 | |
| #include "nsCSSAnonBoxes.h"
 | |
| #include "nsCSSRendering.h"
 | |
| #include "nsDisplayList.h"
 | |
| #include "nsGkAtoms.h"
 | |
| #include "nsIFrameInlines.h"
 | |
| #include "nsLayoutUtils.h"
 | |
| #include "nsLegendFrame.h"
 | |
| #include "nsStyleConsts.h"
 | |
| 
 | |
| using namespace mozilla;
 | |
| using namespace mozilla::gfx;
 | |
| using namespace mozilla::layout;
 | |
| 
 | |
| nsContainerFrame*
 | |
| NS_NewFieldSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 | |
| {
 | |
|   return new (aPresShell) nsFieldSetFrame(aContext);
 | |
| }
 | |
| 
 | |
| NS_IMPL_FRAMEARENA_HELPERS(nsFieldSetFrame)
 | |
| 
 | |
| nsFieldSetFrame::nsFieldSetFrame(nsStyleContext* aContext)
 | |
|   : nsContainerFrame(aContext, kClassID)
 | |
|   , mLegendRect(GetWritingMode())
 | |
| {
 | |
|   mLegendSpace  = 0;
 | |
| }
 | |
| 
 | |
| nsRect
 | |
| nsFieldSetFrame::VisualBorderRectRelativeToSelf() const
 | |
| {
 | |
|   WritingMode wm = GetWritingMode();
 | |
|   Side legendSide = wm.PhysicalSide(eLogicalSideBStart);
 | |
|   nscoord legendBorder = StyleBorder()->GetComputedBorderWidth(legendSide);
 | |
|   LogicalRect r(wm, LogicalPoint(wm, 0, 0), GetLogicalSize(wm));
 | |
|   nsSize containerSize = r.Size(wm).GetPhysicalSize(wm);
 | |
|   if (legendBorder < mLegendRect.BSize(wm)) {
 | |
|     nscoord off = (mLegendRect.BSize(wm) - legendBorder) / 2;
 | |
|     r.BStart(wm) += off;
 | |
|     r.BSize(wm) -= off;
 | |
|   }
 | |
|   return r.GetPhysicalRect(wm, containerSize);
 | |
| }
 | |
| 
 | |
| nsIFrame*
 | |
| nsFieldSetFrame::GetInner() const
 | |
| {
 | |
|   nsIFrame* last = mFrames.LastChild();
 | |
|   if (last &&
 | |
|       last->StyleContext()->GetPseudo() == nsCSSAnonBoxes::fieldsetContent) {
 | |
|     return last;
 | |
|   }
 | |
|   MOZ_ASSERT(mFrames.LastChild() == mFrames.FirstChild());
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| nsIFrame*
 | |
| nsFieldSetFrame::GetLegend() const
 | |
| {
 | |
|   if (mFrames.FirstChild() == GetInner()) {
 | |
|     MOZ_ASSERT(mFrames.LastChild() == mFrames.FirstChild());
 | |
|     return nullptr;
 | |
|   }
 | |
|   MOZ_ASSERT(mFrames.FirstChild() &&
 | |
|              mFrames.FirstChild()->GetContentInsertionFrame()->IsLegendFrame());
 | |
|   return mFrames.FirstChild();
 | |
| }
 | |
| 
 | |
| class nsDisplayFieldSetBorder : public nsDisplayItem {
 | |
| public:
 | |
|   nsDisplayFieldSetBorder(nsDisplayListBuilder* aBuilder,
 | |
|                           nsFieldSetFrame* aFrame)
 | |
|     : nsDisplayItem(aBuilder, aFrame) {
 | |
|     MOZ_COUNT_CTOR(nsDisplayFieldSetBorder);
 | |
|   }
 | |
| #ifdef NS_BUILD_REFCNT_LOGGING
 | |
|   virtual ~nsDisplayFieldSetBorder() {
 | |
|     MOZ_COUNT_DTOR(nsDisplayFieldSetBorder);
 | |
|   }
 | |
| #endif
 | |
|   virtual void Paint(nsDisplayListBuilder* aBuilder,
 | |
|                      gfxContext* aCtx) override;
 | |
|   virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
 | |
|   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
 | |
|                                          const nsDisplayItemGeometry* aGeometry,
 | |
|                                          nsRegion *aInvalidRegion) override;
 | |
|   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
 | |
|   NS_DISPLAY_DECL_NAME("FieldSetBorder", TYPE_FIELDSET_BORDER_BACKGROUND)
 | |
| };
 | |
| 
 | |
| void
 | |
| nsDisplayFieldSetBorder::Paint(nsDisplayListBuilder* aBuilder,
 | |
|                                gfxContext* aCtx)
 | |
| {
 | |
|   image::DrawResult result = static_cast<nsFieldSetFrame*>(mFrame)->
 | |
|     PaintBorder(aBuilder, *aCtx, ToReferenceFrame(), mVisibleRect);
 | |
| 
 | |
|   nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
 | |
| }
 | |
| 
 | |
| nsDisplayItemGeometry*
 | |
| nsDisplayFieldSetBorder::AllocateGeometry(nsDisplayListBuilder* aBuilder)
 | |
| {
 | |
|   return new nsDisplayItemGenericImageGeometry(this, aBuilder);
 | |
| }
 | |
| 
 | |
| void
 | |
| nsDisplayFieldSetBorder::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
 | |
|                                                    const nsDisplayItemGeometry* aGeometry,
 | |
|                                                    nsRegion *aInvalidRegion)
 | |
| {
 | |
|   auto geometry =
 | |
|     static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
 | |
| 
 | |
|   if (aBuilder->ShouldSyncDecodeImages() &&
 | |
|       geometry->ShouldInvalidateToSyncDecodeImages()) {
 | |
|     bool snap;
 | |
|     aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
 | |
|   }
 | |
| 
 | |
|   nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
 | |
| }
 | |
| 
 | |
| nsRect
 | |
| nsDisplayFieldSetBorder::GetBounds(nsDisplayListBuilder* aBuilder,
 | |
|                                    bool* aSnap)
 | |
| {
 | |
|   // Just go ahead and claim our frame's overflow rect as the bounds, because we
 | |
|   // may have border-image-outset or other features that cause borders to extend
 | |
|   // outside the border rect.  We could try to duplicate all the complexity
 | |
|   // nsDisplayBorder has here, but keeping things in sync would be a pain, and
 | |
|   // this code is not typically performance-sensitive.
 | |
|   *aSnap = false;
 | |
|   return Frame()->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
 | |
| }
 | |
| 
 | |
| void
 | |
| nsFieldSetFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
 | |
|                                   const nsRect&           aDirtyRect,
 | |
|                                   const nsDisplayListSet& aLists) {
 | |
|   // Paint our background and border in a special way.
 | |
|   // REVIEW: We don't really need to check frame emptiness here; if it's empty,
 | |
|   // the background/border display item won't do anything, and if it isn't empty,
 | |
|   // we need to paint the outline
 | |
|   if (!(GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) &&
 | |
|       IsVisibleForPainting(aBuilder)) {
 | |
|     if (StyleEffects()->mBoxShadow) {
 | |
|       aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
 | |
|         nsDisplayBoxShadowOuter(aBuilder, this));
 | |
|     }
 | |
| 
 | |
|     nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
 | |
|       aBuilder, this, VisualBorderRectRelativeToSelf(),
 | |
|       aLists.BorderBackground(),
 | |
|       /* aAllowWillPaintBorderOptimization = */ false);
 | |
| 
 | |
|     aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
 | |
|       nsDisplayFieldSetBorder(aBuilder, this));
 | |
|   
 | |
|     DisplayOutlineUnconditional(aBuilder, aLists);
 | |
| 
 | |
|     DO_GLOBAL_REFLOW_COUNT_DSP("nsFieldSetFrame");
 | |
|   }
 | |
| 
 | |
|   if (GetPrevInFlow()) {
 | |
|     DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
 | |
|   }
 | |
| 
 | |
|   nsDisplayListCollection contentDisplayItems;
 | |
|   if (nsIFrame* inner = GetInner()) {
 | |
|     // Collect the inner frame's display items into their own collection.
 | |
|     // We need to be calling BuildDisplayList on it before the legend in
 | |
|     // case it contains out-of-flow frames whose placeholders are in the
 | |
|     // legend. However, we want the inner frame's display items to be
 | |
|     // after the legend's display items in z-order, so we need to save them
 | |
|     // and append them later.
 | |
|     BuildDisplayListForChild(aBuilder, inner, aDirtyRect, contentDisplayItems);
 | |
|   }
 | |
|   if (nsIFrame* legend = GetLegend()) {
 | |
|     // The legend's background goes on our BlockBorderBackgrounds list because
 | |
|     // it's a block child.
 | |
|     nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds());
 | |
|     BuildDisplayListForChild(aBuilder, legend, aDirtyRect, set);
 | |
|   }
 | |
|   // Put the inner frame's display items on the master list. Note that this
 | |
|   // moves its border/background display items to our BorderBackground() list,
 | |
|   // which isn't really correct, but it's OK because the inner frame is
 | |
|   // anonymous and can't have its own border and background.
 | |
|   contentDisplayItems.MoveTo(aLists);
 | |
| }
 | |
| 
 | |
| image::DrawResult
 | |
| nsFieldSetFrame::PaintBorder(
 | |
|   nsDisplayListBuilder* aBuilder,
 | |
|   gfxContext& aRenderingContext,
 | |
|   nsPoint aPt,
 | |
|   const nsRect& aDirtyRect)
 | |
| {
 | |
|   // If the border is smaller than the legend, move the border down
 | |
|   // to be centered on the legend.  We call VisualBorderRectRelativeToSelf() to
 | |
|   // compute the border positioning.
 | |
|   // FIXME: This means border-radius clamping is incorrect; we should
 | |
|   // override nsIFrame::GetBorderRadii.
 | |
|   nsRect rect = VisualBorderRectRelativeToSelf() + aPt;
 | |
|   nsPresContext* presContext = PresContext();
 | |
| 
 | |
|   PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages()
 | |
|                                ? PaintBorderFlags::SYNC_DECODE_IMAGES
 | |
|                                : PaintBorderFlags();
 | |
| 
 | |
|   DrawResult result = DrawResult::SUCCESS;
 | |
| 
 | |
|   nsCSSRendering::PaintBoxShadowInner(presContext, aRenderingContext,
 | |
|                                       this, rect);
 | |
| 
 | |
|   if (nsIFrame* legend = GetLegend()) {
 | |
|     // We want to avoid drawing our border under the legend, so clip out the
 | |
|     // legend while drawing our border.  We don't want to use mLegendRect here,
 | |
|     // because we do want to draw our border under the legend's inline-start and
 | |
|     // -end margins.  And we use GetNormalRect(), not GetRect(), because we do
 | |
|     // not want relative positioning applied to the legend to change how our
 | |
|     // border looks.
 | |
|     nsRect legendRect = legend->GetNormalRect() + aPt;
 | |
| 
 | |
|     DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
 | |
|     // We set up a clip path which has our rect clockwise and the legend rect
 | |
|     // counterclockwise, with FILL_WINDING as the fill rule.  That will allow us
 | |
|     // to paint within our rect but outside the legend rect.  For "our rect" we
 | |
|     // use our visual overflow rect (relative to ourselves, so it's not affected
 | |
|     // by transforms), because we can have borders sticking outside our border
 | |
|     // box (e.g. due to border-image-outset).
 | |
|     RefPtr<PathBuilder> pathBuilder =
 | |
|       drawTarget->CreatePathBuilder(FillRule::FILL_WINDING);
 | |
|     int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
 | |
|     AppendRectToPath(pathBuilder,
 | |
|                      NSRectToSnappedRect(GetVisualOverflowRectRelativeToSelf() + aPt,
 | |
|                                          appUnitsPerDevPixel,
 | |
|                                          *drawTarget),
 | |
|                      true);
 | |
|     AppendRectToPath(pathBuilder,
 | |
|                      NSRectToSnappedRect(legendRect, appUnitsPerDevPixel,
 | |
|                                          *drawTarget),
 | |
|                      false);
 | |
|     RefPtr<Path> clipPath = pathBuilder->Finish();
 | |
| 
 | |
|     aRenderingContext.Save();
 | |
|     aRenderingContext.Clip(clipPath);
 | |
|     result &=
 | |
|       nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
 | |
|                                   aDirtyRect, rect, mStyleContext, borderFlags);
 | |
|     aRenderingContext.Restore();
 | |
|   } else {
 | |
|     result &=
 | |
|       nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
 | |
|                                   aDirtyRect, nsRect(aPt, mRect.Size()),
 | |
|                                   mStyleContext, borderFlags);
 | |
|   }
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| nscoord
 | |
| nsFieldSetFrame::GetIntrinsicISize(gfxContext* aRenderingContext,
 | |
|                                    nsLayoutUtils::IntrinsicISizeType aType)
 | |
| {
 | |
|   nscoord legendWidth = 0;
 | |
|   nscoord contentWidth = 0;
 | |
|   if (nsIFrame* legend = GetLegend()) {
 | |
|     legendWidth =
 | |
|       nsLayoutUtils::IntrinsicForContainer(aRenderingContext, legend, aType);
 | |
|   }
 | |
| 
 | |
|   if (nsIFrame* inner = GetInner()) {
 | |
|     // Ignore padding on the inner, since the padding will be applied to the
 | |
|     // outer instead, and the padding computed for the inner is wrong
 | |
|     // for percentage padding.
 | |
|     contentWidth =
 | |
|       nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, aType,
 | |
|                                            nsLayoutUtils::IGNORE_PADDING);
 | |
|   }
 | |
| 
 | |
|   return std::max(legendWidth, contentWidth);
 | |
| }
 | |
| 
 | |
| 
 | |
| nscoord
 | |
| nsFieldSetFrame::GetMinISize(gfxContext* aRenderingContext)
 | |
| {
 | |
|   nscoord result = 0;
 | |
|   DISPLAY_MIN_WIDTH(this, result);
 | |
| 
 | |
|   result = GetIntrinsicISize(aRenderingContext, nsLayoutUtils::MIN_ISIZE);
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| nscoord
 | |
| nsFieldSetFrame::GetPrefISize(gfxContext* aRenderingContext)
 | |
| {
 | |
|   nscoord result = 0;
 | |
|   DISPLAY_PREF_WIDTH(this, result);
 | |
| 
 | |
|   result = GetIntrinsicISize(aRenderingContext, nsLayoutUtils::PREF_ISIZE);
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| /* virtual */
 | |
| 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.Reset();
 | |
| 
 | |
|   nsOverflowAreas ocBounds;
 | |
|   nsReflowStatus ocStatus;
 | |
|   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;
 | |
| 
 | |
|   // 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 just start-align it.
 | |
|       mLegendRect.IStart(wm) = innerContentRect.IStart(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);
 | |
|   aStatus.MergeCompletionStatusFrom(ocStatus);
 | |
| 
 | |
|   FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus);
 | |
| 
 | |
|   InvalidateFrame();
 | |
| 
 | |
|   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
 | |
| }
 | |
| 
 | |
| #ifdef DEBUG
 | |
| void
 | |
| nsFieldSetFrame::SetInitialChildList(ChildListID    aListID,
 | |
|                                      nsFrameList&   aChildList)
 | |
| {
 | |
|   nsContainerFrame::SetInitialChildList(aListID, aChildList);
 | |
|   MOZ_ASSERT(aListID != kPrincipalList || GetInner(),
 | |
|              "Setting principal child list should populate our inner frame");
 | |
| }
 | |
| void
 | |
| nsFieldSetFrame::AppendFrames(ChildListID    aListID,
 | |
|                               nsFrameList&   aFrameList)
 | |
| {
 | |
|   MOZ_CRASH("nsFieldSetFrame::AppendFrames not supported");
 | |
| }
 | |
| 
 | |
| void
 | |
| nsFieldSetFrame::InsertFrames(ChildListID    aListID,
 | |
|                               nsIFrame*      aPrevFrame,
 | |
|                               nsFrameList&   aFrameList)
 | |
| {
 | |
|   MOZ_CRASH("nsFieldSetFrame::InsertFrames not supported");
 | |
| }
 | |
| 
 | |
| void
 | |
| nsFieldSetFrame::RemoveFrame(ChildListID    aListID,
 | |
|                              nsIFrame*      aOldFrame)
 | |
| {
 | |
|   MOZ_CRASH("nsFieldSetFrame::RemoveFrame not supported");
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef ACCESSIBILITY
 | |
| a11y::AccType
 | |
| nsFieldSetFrame::AccessibleType()
 | |
| {
 | |
|   return a11y::eHTMLGroupboxType;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| nscoord
 | |
| nsFieldSetFrame::GetLogicalBaseline(WritingMode aWM) const
 | |
| {
 | |
|   switch (StyleDisplay()->mDisplay) {
 | |
|     case mozilla::StyleDisplay::Grid:
 | |
|     case mozilla::StyleDisplay::InlineGrid:
 | |
|     case mozilla::StyleDisplay::Flex:
 | |
|     case mozilla::StyleDisplay::InlineFlex:
 | |
|       return BaselineBOffset(aWM, BaselineSharingGroup::eFirst,
 | |
|                              AlignmentContext::eInline);
 | |
|     default:
 | |
|       return BSize(aWM) - BaselineBOffset(aWM, BaselineSharingGroup::eLast,
 | |
|                                           AlignmentContext::eInline);
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsFieldSetFrame::GetVerticalAlignBaseline(WritingMode aWM,
 | |
|                                           nscoord* aBaseline) const
 | |
| {
 | |
|   nsIFrame* inner = GetInner();
 | |
|   MOZ_ASSERT(!inner->GetWritingMode().IsOrthogonalTo(aWM));
 | |
|   if (!inner->GetVerticalAlignBaseline(aWM, aBaseline)) {
 | |
|     return false;
 | |
|   }
 | |
|   nscoord innerBStart = inner->BStart(aWM, GetSize());
 | |
|   *aBaseline += innerBStart;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsFieldSetFrame::GetNaturalBaselineBOffset(WritingMode          aWM,
 | |
|                                            BaselineSharingGroup aBaselineGroup,
 | |
|                                            nscoord*             aBaseline) const
 | |
| {
 | |
|   nsIFrame* inner = GetInner();
 | |
|   MOZ_ASSERT(!inner->GetWritingMode().IsOrthogonalTo(aWM));
 | |
|   if (!inner->GetNaturalBaselineBOffset(aWM, aBaselineGroup, aBaseline)) {
 | |
|     return false;
 | |
|   }
 | |
|   nscoord innerBStart = inner->BStart(aWM, GetSize());
 | |
|   if (aBaselineGroup == BaselineSharingGroup::eFirst) {
 | |
|     *aBaseline += innerBStart;
 | |
|   } else {
 | |
|     *aBaseline += BSize(aWM) - (innerBStart + inner->BSize(aWM));
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsFieldSetFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoStyleSet& aStyleSet,
 | |
|                                                nsStyleChangeList& aChangeList,
 | |
|                                                nsChangeHint aHintForThisFrame)
 | |
| {
 | |
|   nsIFrame* kid = GetInner();
 | |
|   if (kid) {
 | |
|     UpdateStyleOfChildAnonBox(kid, aStyleSet, aChangeList, aHintForThisFrame);
 | |
|   }
 | |
| }
 | |
| 
 | 
