diff options
Diffstat (limited to 'layout/generic')
-rw-r--r-- | layout/generic/ReflowInput.cpp | 12 | ||||
-rw-r--r-- | layout/generic/ReflowInput.h | 4 | ||||
-rw-r--r-- | layout/generic/Selection.h | 3 | ||||
-rw-r--r-- | layout/generic/nsFlexContainerFrame.cpp | 155 | ||||
-rw-r--r-- | layout/generic/nsFlexContainerFrame.h | 15 | ||||
-rw-r--r-- | layout/generic/nsFrame.cpp | 2 | ||||
-rw-r--r-- | layout/generic/nsGridContainerFrame.cpp | 109 | ||||
-rw-r--r-- | layout/generic/nsIFrame.h | 8 | ||||
-rw-r--r-- | layout/generic/nsSelection.cpp | 12 | ||||
-rw-r--r-- | layout/generic/nsTextFrame.cpp | 6 |
10 files changed, 264 insertions, 62 deletions
diff --git a/layout/generic/ReflowInput.cpp b/layout/generic/ReflowInput.cpp index 42f4a24b5..a8756cea2 100644 --- a/layout/generic/ReflowInput.cpp +++ b/layout/generic/ReflowInput.cpp @@ -93,6 +93,9 @@ ReflowInput::ReflowInput(nsPresContext* aPresContext, if (aFlags & B_CLAMP_MARGIN_BOX_MIN_SIZE) { mFlags.mBClampMarginBoxMinSize = true; } + if (aFlags & I_APPLY_AUTO_MIN_SIZE) { + mFlags.mApplyAutoMinSize = true; + } if (!(aFlags & CALLER_WILL_INIT)) { Init(aPresContext); @@ -242,6 +245,7 @@ ReflowInput::ReflowInput( mFlags.mIOffsetsNeedCSSAlign = mFlags.mBOffsetsNeedCSSAlign = false; mFlags.mIClampMarginBoxMinSize = !!(aFlags & I_CLAMP_MARGIN_BOX_MIN_SIZE); mFlags.mBClampMarginBoxMinSize = !!(aFlags & B_CLAMP_MARGIN_BOX_MIN_SIZE); + mFlags.mApplyAutoMinSize = !!(aFlags & I_APPLY_AUTO_MIN_SIZE); mDiscoveredClearance = nullptr; mPercentBSizeObserver = (aParentReflowInput.mPercentBSizeObserver && @@ -1662,6 +1666,10 @@ ReflowInput::InitAbsoluteConstraints(nsPresContext* aPresContext, computeSizeFlags = ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eBClampMarginBoxMinSize); } + if (mFlags.mApplyAutoMinSize) { + computeSizeFlags = ComputeSizeFlags(computeSizeFlags | + ComputeSizeFlags::eIApplyAutoMinSize); + } if (mFlags.mShrinkWrap) { computeSizeFlags = ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); @@ -2375,6 +2383,10 @@ ReflowInput::InitConstraints(nsPresContext* aPresContext, computeSizeFlags = ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eBClampMarginBoxMinSize); } + if (mFlags.mApplyAutoMinSize) { + computeSizeFlags = ComputeSizeFlags(computeSizeFlags | + ComputeSizeFlags::eIApplyAutoMinSize); + } if (mFlags.mShrinkWrap) { computeSizeFlags = ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); diff --git a/layout/generic/ReflowInput.h b/layout/generic/ReflowInput.h index e42508646..09c980b72 100644 --- a/layout/generic/ReflowInput.h +++ b/layout/generic/ReflowInput.h @@ -220,6 +220,7 @@ public: uint32_t mStaticPosIsCBOrigin:1; // the STATIC_POS_IS_CB_ORIGIN ctor flag uint32_t mIClampMarginBoxMinSize:1; // the I_CLAMP_MARGIN_BOX_MIN_SIZE ctor flag uint32_t mBClampMarginBoxMinSize:1; // the B_CLAMP_MARGIN_BOX_MIN_SIZE ctor flag + uint32_t mApplyAutoMinSize : 1; // the I_APPLY_AUTO_MIN_SIZE ctor flag // If set, the following two flags indicate that: // (1) this frame is absolutely-positioned (or fixed-positioned). @@ -738,6 +739,9 @@ public: // Pass ComputeSizeFlags::eBClampMarginBoxMinSize to ComputeSize(). B_CLAMP_MARGIN_BOX_MIN_SIZE = (1<<6), + + // Pass ComputeSizeFlags::eIApplyAutoMinSize to ComputeSize(). + I_APPLY_AUTO_MIN_SIZE = (1<<7), }; // This method initializes various data members. It is automatically diff --git a/layout/generic/Selection.h b/layout/generic/Selection.h index 6f94303ca..3d5e334fc 100644 --- a/layout/generic/Selection.h +++ b/layout/generic/Selection.h @@ -179,6 +179,9 @@ public: { return mRanges.Length(); } + + void GetType(nsAString& aOutType) const; + nsRange* GetRangeAt(uint32_t aIndex, mozilla::ErrorResult& aRv); void AddRange(nsRange& aRange, mozilla::ErrorResult& aRv); void RemoveRange(nsRange& aRange, mozilla::ErrorResult& aRv); diff --git a/layout/generic/nsFlexContainerFrame.cpp b/layout/generic/nsFlexContainerFrame.cpp index b61024324..3818d3cb7 100644 --- a/layout/generic/nsFlexContainerFrame.cpp +++ b/layout/generic/nsFlexContainerFrame.cpp @@ -33,6 +33,8 @@ typedef nsFlexContainerFrame::FlexItem FlexItem; typedef nsFlexContainerFrame::FlexLine FlexLine; typedef nsFlexContainerFrame::FlexboxAxisTracker FlexboxAxisTracker; typedef nsFlexContainerFrame::StrutInfo StrutInfo; +typedef nsFlexContainerFrame::CachedMeasuringReflowResult + CachedMeasuringReflowResult; static mozilla::LazyLogModule gFlexContainerLog("nsFlexContainerFrame"); @@ -1756,6 +1758,108 @@ nsFlexContainerFrame:: } } +/** + * A cached result for a measuring reflow. + * + * Right now we only need to cache the available size and the computed height + * for checking that the reflow input is valid, and the height and the ascent + * to be used. This can be extended later if needed. + * + * The assumption here is that a given flex item measurement won't change until + * either the available size or computed height changes, or the flex container + * intrinsic size is marked as dirty (due to a style or DOM change). + * + * In particular the computed height may change between measuring reflows due to + * how the mIsFlexContainerMeasuringReflow flag affects size computation (see + * bug 1336708). + * + * Caching it prevents us from doing exponential reflows in cases of deeply + * nested flex and scroll frames. + * + * We store them in the frame property table for simplicity. + */ +class nsFlexContainerFrame::CachedMeasuringReflowResult +{ + // Members that are part of the cache key: + const LogicalSize mAvailableSize; + const nscoord mComputedHeight; + + // Members that are part of the cache value: + const nscoord mHeight; + const nscoord mAscent; + +public: + CachedMeasuringReflowResult(const ReflowInput& aReflowInput, + const ReflowOutput& aDesiredSize) + : mAvailableSize(aReflowInput.AvailableSize()) + , mComputedHeight(aReflowInput.ComputedHeight()) + , mHeight(aDesiredSize.Height()) + , mAscent(aDesiredSize.BlockStartAscent()) + {} + + bool IsValidFor(const ReflowInput& aReflowInput) const { + return mAvailableSize == aReflowInput.AvailableSize() && + mComputedHeight == aReflowInput.ComputedHeight(); + } + + nscoord Height() const { return mHeight; } + + nscoord Ascent() const { return mAscent; } +}; + +NS_DECLARE_FRAME_PROPERTY_DELETABLE(CachedFlexMeasuringReflow, + CachedMeasuringReflowResult); + +const CachedMeasuringReflowResult& +nsFlexContainerFrame::MeasureAscentAndHeightForFlexItem( + FlexItem& aItem, + nsPresContext* aPresContext, + ReflowInput& aChildReflowInput) +{ + const FrameProperties props = aItem.Frame()->Properties(); + if (const auto* cachedResult = props.Get(CachedFlexMeasuringReflow())) { + if (cachedResult->IsValidFor(aChildReflowInput)) { + return *cachedResult; + } + } + + ReflowOutput childDesiredSize(aChildReflowInput); + nsReflowStatus childReflowStatus; + + const uint32_t flags = NS_FRAME_NO_MOVE_FRAME; + ReflowChild(aItem.Frame(), aPresContext, + childDesiredSize, aChildReflowInput, + 0, 0, flags, childReflowStatus); + aItem.SetHadMeasuringReflow(); + + // XXXdholbert Once we do pagination / splitting, we'll need to actually + // handle incomplete childReflowStatuses. But for now, we give our kids + // unconstrained available height, which means they should always complete. + MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus), + "We gave flex item unconstrained available height, so it " + "should be complete"); + + // Tell the child we're done with its initial reflow. + // (Necessary for e.g. GetBaseline() to work below w/out asserting) + FinishReflowChild(aItem.Frame(), aPresContext, + childDesiredSize, &aChildReflowInput, 0, 0, flags); + + auto result = + new CachedMeasuringReflowResult(aChildReflowInput, childDesiredSize); + + props.Set(CachedFlexMeasuringReflow(), result); + return *result; +} + +/* virtual */ void +nsFlexContainerFrame::MarkIntrinsicISizesDirty() +{ + for (nsIFrame* childFrame : mFrames) { + childFrame->Properties().Delete(CachedFlexMeasuringReflow()); + } + nsContainerFrame::MarkIntrinsicISizesDirty(); +} + nscoord nsFlexContainerFrame:: MeasureFlexItemContentHeight(nsPresContext* aPresContext, @@ -1783,27 +1887,15 @@ nsFlexContainerFrame:: childRIForMeasuringHeight.SetVResize(true); } - ReflowOutput childDesiredSize(childRIForMeasuringHeight); - nsReflowStatus childReflowStatus; - const uint32_t flags = NS_FRAME_NO_MOVE_FRAME; - ReflowChild(aFlexItem.Frame(), aPresContext, - childDesiredSize, childRIForMeasuringHeight, - 0, 0, flags, childReflowStatus); - - MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus), - "We gave flex item unconstrained available height, so it " - "should be complete"); - - FinishReflowChild(aFlexItem.Frame(), aPresContext, - childDesiredSize, &childRIForMeasuringHeight, - 0, 0, flags); + const CachedMeasuringReflowResult& reflowResult = + MeasureAscentAndHeightForFlexItem(aFlexItem, aPresContext, + childRIForMeasuringHeight); - aFlexItem.SetHadMeasuringReflow(); - aFlexItem.SetAscent(childDesiredSize.BlockStartAscent()); + aFlexItem.SetAscent(reflowResult.Ascent()); // Subtract border/padding in vertical axis, to get _just_ // the effective computed value of the "height" property. - nscoord childDesiredHeight = childDesiredSize.Height() - + nscoord childDesiredHeight = reflowResult.Height() - childRIForMeasuringHeight.ComputedPhysicalBorderPadding().TopBottom(); return std::max(0, childDesiredHeight); @@ -3959,25 +4051,10 @@ nsFlexContainerFrame::SizeItemInCrossAxis( // whether any of its ancestors are being resized). aChildReflowInput.SetVResize(true); } - ReflowOutput childDesiredSize(aChildReflowInput); - nsReflowStatus childReflowStatus; - const uint32_t flags = NS_FRAME_NO_MOVE_FRAME; - ReflowChild(aItem.Frame(), aPresContext, - childDesiredSize, aChildReflowInput, - 0, 0, flags, childReflowStatus); - aItem.SetHadMeasuringReflow(); - - // XXXdholbert Once we do pagination / splitting, we'll need to actually - // handle incomplete childReflowStatuses. But for now, we give our kids - // unconstrained available height, which means they should always complete. - MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus), - "We gave flex item unconstrained available height, so it " - "should be complete"); - // Tell the child we're done with its initial reflow. - // (Necessary for e.g. GetBaseline() to work below w/out asserting) - FinishReflowChild(aItem.Frame(), aPresContext, - childDesiredSize, &aChildReflowInput, 0, 0, flags); + // Potentially reflow the item, and get the sizing info. + const CachedMeasuringReflowResult& reflowResult = + MeasureAscentAndHeightForFlexItem(aItem, aPresContext, aChildReflowInput); // Save the sizing info that we learned from this reflow // ----------------------------------------------------- @@ -3989,7 +4066,7 @@ nsFlexContainerFrame::SizeItemInCrossAxis( // so we don't bother with making aAxisTracker pick the cross-axis component // for us.) nscoord crossAxisBorderPadding = aItem.GetBorderPadding().TopBottom(); - if (childDesiredSize.Height() < crossAxisBorderPadding) { + if (reflowResult.Height() < crossAxisBorderPadding) { // Child's requested size isn't large enough for its border/padding! // This is OK for the trivial nsFrame::Reflow() impl, but other frame // classes should know better. So, if we get here, the child had better be @@ -4002,10 +4079,10 @@ nsFlexContainerFrame::SizeItemInCrossAxis( aItem.SetCrossSize(0); } else { // (normal case) - aItem.SetCrossSize(childDesiredSize.Height() - crossAxisBorderPadding); + aItem.SetCrossSize(reflowResult.Height() - crossAxisBorderPadding); } - aItem.SetAscent(childDesiredSize.BlockStartAscent()); + aItem.SetAscent(reflowResult.Ascent()); } void @@ -4295,7 +4372,7 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext, LogicalSize availSize = aReflowInput.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; ReflowInput childReflowInput(aPresContext, aReflowInput, - item->Frame(), availSize); + item->Frame(), availSize); if (!sizeOverride) { // Directly override the computed main-size, by tweaking reflow state: if (aAxisTracker.IsMainAxisHorizontal()) { diff --git a/layout/generic/nsFlexContainerFrame.h b/layout/generic/nsFlexContainerFrame.h index 22b420d85..459ae8e20 100644 --- a/layout/generic/nsFlexContainerFrame.h +++ b/layout/generic/nsFlexContainerFrame.h @@ -56,6 +56,7 @@ public: class FlexLine; class FlexboxAxisTracker; struct StrutInfo; + class CachedMeasuringReflowResult; // nsIFrame overrides void Init(nsIContent* aContent, @@ -66,6 +67,8 @@ public: const nsRect& aDirtyRect, const nsDisplayListSet& aLists) override; + void MarkIntrinsicISizesDirty() override; + virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, @@ -195,6 +198,18 @@ protected: const FlexboxAxisTracker& aAxisTracker); /** + * This method gets a cached measuring reflow for a flex item, or does it and + * caches it. + * + * This avoids exponential reflows, see the comment on + * CachedMeasuringReflowResult. + */ + const CachedMeasuringReflowResult& MeasureAscentAndHeightForFlexItem( + FlexItem& aItem, + nsPresContext* aPresContext, + ReflowInput& aChildReflowInput); + + /** * This method performs a "measuring" reflow to get the content height of * aFlexItem.Frame() (treating it as if it had auto-height), & returns the * resulting height. diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 69791d5c5..418fa16b7 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -4781,7 +4781,7 @@ nsFrame::ComputeSize(nsRenderingContext* aRenderingContext, ComputeISizeValue(aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM), boxSizingToMarginEdgeISize, minISizeCoord, aFlags); - } else if (MOZ_UNLIKELY(isGridItem)) { + } else if (MOZ_UNLIKELY(aFlags & eIApplyAutoMinSize)) { // This implements "Implied Minimum Size of Grid Items". // https://drafts.csswg.org/css-grid/#min-size-auto minISize = std::min(maxISize, GetMinISize(aRenderingContext)); diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp index 71d5bba21..8f117b5ab 100644 --- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -818,8 +818,11 @@ struct nsGridContainerFrame::GridItemInfo // Ditto *-content:[last ]baseline. Mutually exclusive w. eSelfBaseline. eContentBaseline = 0x10, eAllBaselineBits = eIsBaselineAligned | eSelfBaseline | eContentBaseline, + // Should apply Automatic Minimum Size per: + // https://drafts.csswg.org/css-grid/#min-size-auto + eApplyAutoMinSize = 0x20, // Clamp per https://drafts.csswg.org/css-grid/#min-size-auto - eClampMarginBoxMinSize = 0x20, + eClampMarginBoxMinSize = 0x40, }; explicit GridItemInfo(nsIFrame* aFrame, @@ -851,11 +854,11 @@ struct nsGridContainerFrame::GridItemInfo return aAlign; } - // Return true if we should we clamp this item's Automatic Minimum Size. + // Return true if we should apply Automatic Minimum Size to this item. // https://drafts.csswg.org/css-grid/#min-size-auto - bool ShouldClampMinSize(WritingMode aContainerWM, - LogicalAxis aContainerAxis, - nscoord aPercentageBasis) const + bool ShouldApplyAutoMinSize(WritingMode aContainerWM, + LogicalAxis aContainerAxis, + nscoord aPercentageBasis) const { const auto pos = mFrame->StylePosition(); const auto& size = aContainerAxis == eLogicalAxisInline ? @@ -2090,6 +2093,18 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput SizingConstraint aConstraint); /** + * Return the percentage basis for a grid item in its writing-mode. + * If aAxis is eLogicalAxisInline then we return NS_UNCONSTRAINEDSIZE in + * both axes since we know all track sizes are indefinite at this point + * (we calculate column sizes before row sizes). Otherwise, assert that + * column sizes are known and calculate the size for aGridItem.mArea.mCols + * and use NS_UNCONSTRAINEDSIZE in the other axis. + * @param aAxis the axis we're currently calculating track sizes for + */ + LogicalSize PercentageBasisFor(LogicalAxis aAxis, + const GridItemInfo& aGridItem) const; + + /** * Return the containing block for a grid item occupying aArea. */ LogicalRect ContainingBlockFor(const GridArea& aArea) const; @@ -3731,18 +3746,20 @@ MeasuringReflow(nsIFrame* aChild, * the child's margin-box) in aAxis. */ static nscoord -ContentContribution(const GridItemInfo& aGridItem, - const GridReflowInput& aState, - nsRenderingContext* aRC, - WritingMode aCBWM, - LogicalAxis aAxis, - IntrinsicISizeType aConstraint, - nscoord aMinSizeClamp = NS_MAXSIZE, - uint32_t aFlags = 0) +ContentContribution(const GridItemInfo& aGridItem, + const GridReflowInput& aState, + nsRenderingContext* aRC, + WritingMode aCBWM, + LogicalAxis aAxis, + const Maybe<LogicalSize>& aPercentageBasis, + IntrinsicISizeType aConstraint, + nscoord aMinSizeClamp = NS_MAXSIZE, + uint32_t aFlags = 0) { nsIFrame* child = aGridItem.mFrame; PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis)); nscoord size = nsLayoutUtils::IntrinsicForAxis(axis, aRC, child, aConstraint, + aPercentageBasis, aFlags | nsLayoutUtils::BAIL_IF_REFLOW_NEEDED | nsLayoutUtils::ADD_PERCENTS, aMinSizeClamp); @@ -3812,6 +3829,10 @@ struct CachedIntrinsicSizes Maybe<nscoord> mMinSize; Maybe<nscoord> mMinContentContribution; Maybe<nscoord> mMaxContentContribution; + + // The item's percentage basis for intrinsic sizing purposes. + Maybe<LogicalSize> mPercentageBasis; + // "if the grid item spans only grid tracks that have a fixed max track // sizing function, its automatic minimum size in that dimension is // further clamped to less than or equal to the size necessary to fit its @@ -3832,7 +3853,11 @@ MinContentContribution(const GridItemInfo& aGridItem, if (aCache->mMinContentContribution.isSome()) { return aCache->mMinContentContribution.value(); } + if (aCache->mPercentageBasis.isNothing()) { + aCache->mPercentageBasis.emplace(aState.PercentageBasisFor(aAxis, aGridItem)); + } nscoord s = ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis, + aCache->mPercentageBasis, nsLayoutUtils::MIN_ISIZE, aCache->mMinSizeClamp); aCache->mMinContentContribution.emplace(s); @@ -3850,7 +3875,11 @@ MaxContentContribution(const GridItemInfo& aGridItem, if (aCache->mMaxContentContribution.isSome()) { return aCache->mMaxContentContribution.value(); } + if (aCache->mPercentageBasis.isNothing()) { + aCache->mPercentageBasis.emplace(aState.PercentageBasisFor(aAxis, aGridItem)); + } nscoord s = ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis, + aCache->mPercentageBasis, nsLayoutUtils::PREF_ISIZE, aCache->mMinSizeClamp); aCache->mMaxContentContribution.emplace(s); @@ -3904,7 +3933,11 @@ MinSize(const GridItemInfo& aGridItem, child->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE)) { // Now calculate the "content size" part and return whichever is smaller. MOZ_ASSERT(unit != eStyleUnit_Enumerated || sz == NS_UNCONSTRAINEDSIZE); + if (aCache->mPercentageBasis.isNothing()) { + aCache->mPercentageBasis.emplace(aState.PercentageBasisFor(aAxis, aGridItem)); + } sz = std::min(sz, ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis, + aCache->mPercentageBasis, nsLayoutUtils::MIN_ISIZE, aCache->mMinSizeClamp, nsLayoutUtils::MIN_INTRINSIC_ISIZE)); @@ -3977,9 +4010,9 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSizeStep1( WritingMode wm = aState.mWM; // Calculate data for "Automatic Minimum Size" clamping, if needed. bool needed = ((sz.mState & TrackSize::eIntrinsicMinSizing) || - aConstraint == SizingConstraint::eNoConstraint); - if (needed && TrackSize::IsDefiniteMaxSizing(sz.mState) && - aGridItem.ShouldClampMinSize(wm, mAxis, aPercentageBasis)) { + aConstraint == SizingConstraint::eNoConstraint) && + (aGridItem.mState[mAxis] & ItemState::eApplyAutoMinSize); + if (needed && TrackSize::IsDefiniteMaxSizing(sz.mState)) { if (sz.mState & TrackSize::eIntrinsicMinSizing) { auto maxCoord = aFunctions.MaxSizingFor(aRange.mStart); cache.mMinSizeClamp = @@ -4382,6 +4415,14 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize( iter.Reset(); for (; !iter.AtEnd(); iter.Next()) { auto& gridItem = aGridItems[iter.GridItemIndex()]; + + // Check if we need to apply "Automatic Minimum Size" and cache it. + MOZ_ASSERT(!(gridItem.mState[mAxis] & ItemState::eApplyAutoMinSize), + "Why is eApplyAutoMinSize set already?"); + if (gridItem.ShouldApplyAutoMinSize(wm, mAxis, aPercentageBasis)) { + gridItem.mState[mAxis] |= ItemState::eApplyAutoMinSize; + } + const GridArea& area = gridItem.mArea; const LineRange& lineRange = area.*aRange; uint32_t span = lineRange.Extent(); @@ -4407,9 +4448,9 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize( CachedIntrinsicSizes cache; // Calculate data for "Automatic Minimum Size" clamping, if needed. bool needed = ((state & TrackSize::eIntrinsicMinSizing) || - aConstraint == SizingConstraint::eNoConstraint); - if (needed && TrackSize::IsDefiniteMaxSizing(state) && - gridItem.ShouldClampMinSize(wm, mAxis, aPercentageBasis)) { + aConstraint == SizingConstraint::eNoConstraint) && + (gridItem.mState[mAxis] & ItemState::eApplyAutoMinSize); + if (needed && TrackSize::IsDefiniteMaxSizing(state)) { nscoord minSizeClamp = 0; for (auto i = lineRange.mStart, end = lineRange.mEnd; i < end; ++i) { auto maxCoord = aFunctions.MaxSizingFor(i); @@ -4445,11 +4486,14 @@ nsGridContainerFrame::Tracks::ResolveIntrinsicSize( gridItem.mState[mAxis] |= ItemState::eIsFlexing; } else if (aConstraint == SizingConstraint::eNoConstraint && TrackSize::IsDefiniteMaxSizing(state) && - gridItem.ShouldClampMinSize(wm, mAxis, aPercentageBasis)) { + (gridItem.mState[mAxis] & ItemState::eApplyAutoMinSize)) { gridItem.mState[mAxis] |= ItemState::eClampMarginBoxMinSize; } } } + MOZ_ASSERT(!(gridItem.mState[mAxis] & ItemState::eClampMarginBoxMinSize) || + (gridItem.mState[mAxis] & ItemState::eApplyAutoMinSize), + "clamping only applies to Automatic Minimum Size"); } // Step 2. @@ -4707,7 +4751,8 @@ nsGridContainerFrame::Tracks::FindUsedFlexFraction( const GridItemInfo& item = aGridItems[iter.GridItemIndex()]; if (item.mState[mAxis] & ItemState::eIsFlexing) { // XXX optimize: bug 1194446 - nscoord spaceToFill = ContentContribution(item, aState, rc, wm, mAxis, + auto pb = Some(aState.PercentageBasisFor(mAxis, item)); + nscoord spaceToFill = ContentContribution(item, aState, rc, wm, mAxis, pb, nsLayoutUtils::PREF_ISIZE); if (spaceToFill <= 0) { continue; @@ -5038,6 +5083,25 @@ nsGridContainerFrame::LineRange::ToPositionAndLengthForAbsPos( } } +LogicalSize +nsGridContainerFrame::GridReflowInput::PercentageBasisFor( + LogicalAxis aAxis, + const GridItemInfo& aGridItem) const +{ + auto wm = aGridItem.mFrame->GetWritingMode(); + if (aAxis == eLogicalAxisInline) { + return LogicalSize(wm, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); + } + // Note: for now, we only resolve transferred percentages to row sizing. + // We may need to adjust these assertions once we implement bug 1300366. + MOZ_ASSERT(mCols.mCanResolveLineRangeSize); + MOZ_ASSERT(!mRows.mCanResolveLineRangeSize); + nscoord colSize = aGridItem.mArea.mCols.ToLength(mCols.mSizes); + nscoord rowSize = NS_UNCONSTRAINEDSIZE; + return !wm.IsOrthogonalTo(mWM) ? + LogicalSize(wm, colSize, rowSize) : LogicalSize(wm, rowSize, colSize); +} + LogicalRect nsGridContainerFrame::GridReflowInput::ContainingBlockFor(const GridArea& aArea) const { @@ -5231,6 +5295,9 @@ nsGridContainerFrame::ReflowInFlowChild(nsIFrame* aChild, } else { aChild->Properties().Delete(BClampMarginBoxMinSizeProperty()); } + if ((aGridItemInfo->mState[childIAxis] & ItemState::eApplyAutoMinSize)) { + flags |= ReflowInput::I_APPLY_AUTO_MIN_SIZE; + } } if (!isConstrainedBSize) { diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 50eb958e0..2acafa882 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -2105,6 +2105,14 @@ public: */ eIClampMarginBoxMinSize = 1 << 2, // clamp in our inline axis eBClampMarginBoxMinSize = 1 << 3, // clamp in our block axis + /** + * The frame is stretching (per CSS Box Alignment) and doesn't have an + * Automatic Minimum Size in the indicated axis. + * (may be used for both flex/grid items, but currently only used for Grid) + * https://drafts.csswg.org/css-grid/#min-size-auto + * https://drafts.csswg.org/css-align-3/#valdef-justify-self-stretch + */ + eIApplyAutoMinSize = 1 << 4, // only has an effect when eShrinkWrap is false }; /** diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index e0d65632e..a2227c39c 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -5349,6 +5349,18 @@ Selection::GetRangeCount(int32_t* aRangeCount) return NS_OK; } +void +Selection::GetType(nsAString& aOutType) const +{ + if (!RangeCount()) { + aOutType.AssignLiteral("None"); + } else if (IsCollapsed()) { + aOutType.AssignLiteral("Caret"); + } else { + aOutType.AssignLiteral("Range"); + } +} + NS_IMETHODIMP Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn) { diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index b9848bcf1..00c0016fd 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -9751,9 +9751,13 @@ nsTextFrame::GetRenderedText(uint32_t aStartOffset, startOffset = aStartOffset; endOffset = std::min<uint32_t>(INT32_MAX, aEndOffset); } + + // If startOffset and/or endOffset are inside of trimmedOffsets' range, + // then clamp the edges of trimmedOffsets accordingly. + int32_t origTrimmedOffsetsEnd = trimmedOffsets.GetEnd(); trimmedOffsets.mStart = std::max<uint32_t>(trimmedOffsets.mStart, startOffset); - trimmedOffsets.mLength = std::min<uint32_t>(trimmedOffsets.GetEnd(), + trimmedOffsets.mLength = std::min<uint32_t>(origTrimmedOffsetsEnd, endOffset) - trimmedOffsets.mStart; if (trimmedOffsets.mLength <= 0) { offsetInRenderedString = nextOffsetInRenderedString; |