/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code is subject to the terms of the Mozilla Public License * version 2.0 (the "License"). You can obtain a copy of the License at * http://mozilla.org/MPL/2.0/. */ /* rendering object for CSS "display: grid | inline-grid" */ #ifndef nsGridContainerFrame_h___ #define nsGridContainerFrame_h___ #include "mozilla/Maybe.h" #include "mozilla/TypeTraits.h" #include "nsContainerFrame.h" #include "nsHashKeys.h" #include "nsTHashtable.h" /** * Factory function. * @return a newly allocated nsGridContainerFrame (infallible) */ nsContainerFrame* NS_NewGridContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); namespace mozilla { /** * The number of implicit / explicit tracks and their sizes. */ struct ComputedGridTrackInfo { ComputedGridTrackInfo(uint32_t aNumLeadingImplicitTracks, uint32_t aNumExplicitTracks, uint32_t aStartFragmentTrack, uint32_t aEndFragmentTrack, nsTArray&& aPositions, nsTArray&& aSizes, nsTArray&& aStates, nsTArray&& aRemovedRepeatTracks, uint32_t aRepeatFirstTrack) : mNumLeadingImplicitTracks(aNumLeadingImplicitTracks) , mNumExplicitTracks(aNumExplicitTracks) , mStartFragmentTrack(aStartFragmentTrack) , mEndFragmentTrack(aEndFragmentTrack) , mPositions(aPositions) , mSizes(aSizes) , mStates(aStates) , mRemovedRepeatTracks(aRemovedRepeatTracks) , mRepeatFirstTrack(aRepeatFirstTrack) {} uint32_t mNumLeadingImplicitTracks; uint32_t mNumExplicitTracks; uint32_t mStartFragmentTrack; uint32_t mEndFragmentTrack; nsTArray mPositions; nsTArray mSizes; nsTArray mStates; nsTArray mRemovedRepeatTracks; uint32_t mRepeatFirstTrack; }; struct ComputedGridLineInfo { explicit ComputedGridLineInfo(nsTArray>&& aNames, const nsTArray& aNamesBefore, const nsTArray& aNamesAfter) : mNames(aNames) , mNamesBefore(aNamesBefore) , mNamesAfter(aNamesAfter) {} nsTArray> mNames; nsTArray mNamesBefore; nsTArray mNamesAfter; }; } // namespace mozilla class nsGridContainerFrame final : public nsContainerFrame { public: NS_DECL_FRAMEARENA_HELPERS NS_DECL_QUERYFRAME_TARGET(nsGridContainerFrame) NS_DECL_QUERYFRAME typedef mozilla::ComputedGridTrackInfo ComputedGridTrackInfo; typedef mozilla::ComputedGridLineInfo ComputedGridLineInfo; // nsIFrame overrides void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) override; nscoord GetMinISize(nsRenderingContext* aRenderingContext) override; nscoord GetPrefISize(nsRenderingContext* aRenderingContext) override; void MarkIntrinsicISizesDirty() override; nsIAtom* GetType() const override; bool IsFrameOfType(uint32_t aFlags) const override { return nsContainerFrame::IsFrameOfType(aFlags & ~nsIFrame::eCanContainOverflowContainers); } void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) override; nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const override { if (HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) { // Return a baseline synthesized from our margin-box. return nsContainerFrame::GetLogicalBaseline(aWM); } nscoord b; GetBBaseline(BaselineSharingGroup::eFirst, &b); return b; } bool GetVerticalAlignBaseline(mozilla::WritingMode aWM, nscoord* aBaseline) const override { return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, aBaseline); } bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup, nscoord* aBaseline) const override { if (HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) { return false; } return GetBBaseline(aBaselineGroup, aBaseline); } #ifdef DEBUG_FRAME_DUMP nsresult GetFrameName(nsAString& aResult) const override; #endif // nsContainerFrame overrides bool DrainSelfOverflowList() override; void AppendFrames(ChildListID aListID, nsFrameList& aFrameList) override; void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, nsFrameList& aFrameList) override; void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override; uint16_t CSSAlignmentForAbsPosChild( const ReflowInput& aChildRI, mozilla::LogicalAxis aLogicalAxis) const override; #ifdef DEBUG void SetInitialChildList(ChildListID aListID, nsFrameList& aChildList) override; #endif /** * Return the containing block for aChild which MUST be an abs.pos. child * of a grid container. This is just a helper method for * nsAbsoluteContainingBlock::Reflow - it's not meant to be used elsewhere. */ static const nsRect& GridItemCB(nsIFrame* aChild); NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridItemContainingBlockRect, nsRect) /** * These properties are created by a call to * nsGridContainerFrame::GetGridFrameWithComputedInfo, typically from * Element::GetGridFragments. */ NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridColTrackInfo, ComputedGridTrackInfo) const ComputedGridTrackInfo* GetComputedTemplateColumns() { const ComputedGridTrackInfo* info = GetProperty(GridColTrackInfo()); MOZ_ASSERT(info, "Property generation wasn't requested."); return info; } NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridRowTrackInfo, ComputedGridTrackInfo) const ComputedGridTrackInfo* GetComputedTemplateRows() { const ComputedGridTrackInfo* info = GetProperty(GridRowTrackInfo()); MOZ_ASSERT(info, "Property generation wasn't requested."); return info; } NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridColumnLineInfo, ComputedGridLineInfo) const ComputedGridLineInfo* GetComputedTemplateColumnLines() { const ComputedGridLineInfo* info = GetProperty(GridColumnLineInfo()); MOZ_ASSERT(info, "Property generation wasn't requested."); return info; } NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridRowLineInfo, ComputedGridLineInfo) const ComputedGridLineInfo* GetComputedTemplateRowLines() { const ComputedGridLineInfo* info = GetProperty(GridRowLineInfo()); MOZ_ASSERT(info, "Property generation wasn't requested."); return info; } typedef nsBaseHashtable ImplicitNamedAreas; NS_DECLARE_FRAME_PROPERTY_DELETABLE(ImplicitNamedAreasProperty, ImplicitNamedAreas) ImplicitNamedAreas* GetImplicitNamedAreas() const { return GetProperty(ImplicitNamedAreasProperty()); } typedef nsTArray ExplicitNamedAreas; NS_DECLARE_FRAME_PROPERTY_DELETABLE(ExplicitNamedAreasProperty, ExplicitNamedAreas) ExplicitNamedAreas* GetExplicitNamedAreas() const { return GetProperty(ExplicitNamedAreasProperty()); } /** * Return a containing grid frame, and ensure it has computed grid info * @return nullptr if aFrame has no grid container, or frame was destroyed * @note this might destroy layout/style data since it may flush layout */ static nsGridContainerFrame* GetGridFrameWithComputedInfo(nsIFrame* aFrame); struct TrackSize; struct GridItemInfo; struct GridReflowInput; template class GridItemCSSOrderIteratorT; typedef GridItemCSSOrderIteratorT GridItemCSSOrderIterator; typedef GridItemCSSOrderIteratorT ReverseGridItemCSSOrderIterator; struct FindItemInGridOrderResult { // The first(last) item in (reverse) grid order. const GridItemInfo* mItem; // Does the above item span the first(last) track? bool mIsInEdgeTrack; }; protected: static const uint32_t kAutoLine; // The maximum line number, in the zero-based translated grid. static const uint32_t kTranslatedMaxLine; typedef mozilla::LogicalPoint LogicalPoint; typedef mozilla::LogicalRect LogicalRect; typedef mozilla::LogicalSize LogicalSize; typedef mozilla::WritingMode WritingMode; typedef mozilla::css::GridNamedArea GridNamedArea; typedef mozilla::layout::AutoFrameListPtr AutoFrameListPtr; typedef nsLayoutUtils::IntrinsicISizeType IntrinsicISizeType; struct Grid; struct GridArea; class LineNameMap; struct LineRange; struct SharedGridData; struct TrackSizingFunctions; struct Tracks; struct TranslatedLineRange; friend nsContainerFrame* NS_NewGridContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); explicit nsGridContainerFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) , mCachedMinISize(NS_INTRINSIC_WIDTH_UNKNOWN) , mCachedPrefISize(NS_INTRINSIC_WIDTH_UNKNOWN) { mBaseline[0][0] = NS_INTRINSIC_WIDTH_UNKNOWN; mBaseline[0][1] = NS_INTRINSIC_WIDTH_UNKNOWN; mBaseline[1][0] = NS_INTRINSIC_WIDTH_UNKNOWN; mBaseline[1][1] = NS_INTRINSIC_WIDTH_UNKNOWN; } /** * XXX temporary - move the ImplicitNamedAreas stuff to the style system. * The implicit area names that come from x-start .. x-end lines in * grid-template-columns / grid-template-rows are stored in this frame * property when needed, as a ImplicitNamedAreas* value. */ void InitImplicitNamedAreas(const nsStylePosition* aStyle); void AddImplicitNamedAreas(const nsTArray>& aLineNameLists); /** * Reflow and place our children. * @return the consumed size of all of this grid container's continuations * so far including this frame */ nscoord ReflowChildren(GridReflowInput& aState, const LogicalRect& aContentArea, ReflowOutput& aDesiredSize, nsReflowStatus& aStatus); /** * Helper for GetMinISize / GetPrefISize. */ nscoord IntrinsicISize(nsRenderingContext* aRenderingContext, IntrinsicISizeType aConstraint); // Helper for AppendFrames / InsertFrames. void NoteNewChildren(ChildListID aListID, const nsFrameList& aFrameList); // Helper to move child frames into the kOverflowList. void MergeSortedOverflow(nsFrameList& aList); // Helper to move child frames into the kExcessOverflowContainersList:. void MergeSortedExcessOverflowContainers(nsFrameList& aList); bool GetBBaseline(BaselineSharingGroup aBaselineGroup, nscoord* aResult) const { *aResult = mBaseline[mozilla::eLogicalAxisBlock][aBaselineGroup]; return true; } bool GetIBaseline(BaselineSharingGroup aBaselineGroup, nscoord* aResult) const { *aResult = mBaseline[mozilla::eLogicalAxisInline][aBaselineGroup]; return true; } /** * Calculate this grid container's baselines. * @param aBaselineSet which baseline(s) to derive from a baseline-group or * items; a baseline not included is synthesized from the border-box instead. * @param aFragmentStartTrack is the first track in this fragment in the same * axis as aMajor. Pass zero if that's not the axis we're fragmenting in. * @param aFirstExcludedTrack should be the first track in the next fragment * or one beyond the final track in the last fragment, in aMajor's axis. * Pass the number of tracks if that's not the axis we're fragmenting in. */ enum BaselineSet : uint32_t { eNone = 0x0, eFirst = 0x1, eLast = 0x2, eBoth = eFirst | eLast, }; void CalculateBaselines(BaselineSet aBaselineSet, GridItemCSSOrderIterator* aIter, const nsTArray* aGridItems, const Tracks& aTracks, uint32_t aFragmentStartTrack, uint32_t aFirstExcludedTrack, WritingMode aWM, const nsSize& aCBPhysicalSize, nscoord aCBBorderPaddingStart, nscoord aCBBorderPaddingStartEnd, nscoord aCBSize); /** * Synthesize a Grid container baseline for aGroup. */ nscoord SynthesizeBaseline(const FindItemInGridOrderResult& aItem, mozilla::LogicalAxis aAxis, BaselineSharingGroup aGroup, const nsSize& aCBPhysicalSize, nscoord aCBSize, WritingMode aCBWM); /** * Find the first item in Grid Order in this fragment. * https://drafts.csswg.org/css-grid/#grid-order * @param aFragmentStartTrack is the first track in this fragment in the same * axis as aMajor. Pass zero if that's not the axis we're fragmenting in. */ static FindItemInGridOrderResult FindFirstItemInGridOrder(GridItemCSSOrderIterator& aIter, const nsTArray& aGridItems, LineRange GridArea::* aMajor, LineRange GridArea::* aMinor, uint32_t aFragmentStartTrack); /** * Find the last item in Grid Order in this fragment. * @param aFragmentStartTrack is the first track in this fragment in the same * axis as aMajor. Pass zero if that's not the axis we're fragmenting in. * @param aFirstExcludedTrack should be the first track in the next fragment * or one beyond the final track in the last fragment, in aMajor's axis. * Pass the number of tracks if that's not the axis we're fragmenting in. */ static FindItemInGridOrderResult FindLastItemInGridOrder(ReverseGridItemCSSOrderIterator& aIter, const nsTArray& aGridItems, LineRange GridArea::* aMajor, LineRange GridArea::* aMinor, uint32_t aFragmentStartTrack, uint32_t aFirstExcludedTrack); #ifdef DEBUG void SanityCheckGridItemsBeforeReflow() const; #endif // DEBUG private: // Helpers for ReflowChildren struct Fragmentainer { /** * The distance from the first grid container fragment's block-axis content * edge to the fragmentainer end. */ nscoord mToFragmentainerEnd; /** * True if the current fragment is at the start of the fragmentainer. */ bool mIsTopOfPage; /** * Is there a Class C break opportunity at the start content edge? */ bool mCanBreakAtStart; /** * Is there a Class C break opportunity at the end content edge? */ bool mCanBreakAtEnd; /** * Is the grid container's block-size unconstrained? */ bool mIsAutoBSize; }; mozilla::Maybe GetNearestFragmentainer(const GridReflowInput& aState) const; // @return the consumed size of all continuations so far including this frame nscoord ReflowInFragmentainer(GridReflowInput& aState, const LogicalRect& aContentArea, ReflowOutput& aDesiredSize, nsReflowStatus& aStatus, Fragmentainer& aFragmentainer, const nsSize& aContainerSize); // Helper for ReflowInFragmentainer // @return the consumed size of all continuations so far including this frame nscoord ReflowRowsInFragmentainer(GridReflowInput& aState, const LogicalRect& aContentArea, ReflowOutput& aDesiredSize, nsReflowStatus& aStatus, Fragmentainer& aFragmentainer, const nsSize& aContainerSize, const nsTArray& aItems, uint32_t aStartRow, uint32_t aEndRow, nscoord aBSize, nscoord aAvailableSize); // Helper for ReflowChildren / ReflowInFragmentainer void ReflowInFlowChild(nsIFrame* aChild, const GridItemInfo* aGridItemInfo, nsSize aContainerSize, mozilla::Maybe aStretchBSize, const Fragmentainer* aFragmentainer, const GridReflowInput& aState, const LogicalRect& aContentArea, ReflowOutput& aDesiredSize, nsReflowStatus& aStatus); /** * Cached values to optimize GetMinISize/GetPrefISize. */ nscoord mCachedMinISize; nscoord mCachedPrefISize; // Our baselines, one per BaselineSharingGroup per axis. nscoord mBaseline[2/*LogicalAxis*/][2/*BaselineSharingGroup*/]; #ifdef DEBUG // If true, NS_STATE_GRID_DID_PUSH_ITEMS may be set even though all pushed // frames may have been removed. This is used to suppress an assertion // in case RemoveFrame removed all associated child frames. bool mDidPushItemsBitMayLie; #endif }; #endif /* nsGridContainerFrame_h___ */