1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code is subject to the terms of the Mozilla Public License
4  * version 2.0 (the "License"). You can obtain a copy of the License at
5  * http://mozilla.org/MPL/2.0/. */
6 
7 /* rendering object for CSS "display: grid | inline-grid" */
8 
9 #ifndef nsGridContainerFrame_h___
10 #define nsGridContainerFrame_h___
11 
12 #include "mozilla/Maybe.h"
13 #include "mozilla/TypeTraits.h"
14 #include "nsContainerFrame.h"
15 #include "nsHashKeys.h"
16 #include "nsTHashtable.h"
17 
18 /**
19  * Factory function.
20  * @return a newly allocated nsGridContainerFrame (infallible)
21  */
22 nsContainerFrame* NS_NewGridContainerFrame(nsIPresShell* aPresShell,
23                                            nsStyleContext* aContext);
24 
25 namespace mozilla {
26 /**
27  * The number of implicit / explicit tracks and their sizes.
28  */
29 struct ComputedGridTrackInfo
30 {
ComputedGridTrackInfoComputedGridTrackInfo31   ComputedGridTrackInfo(uint32_t aNumLeadingImplicitTracks,
32                         uint32_t aNumExplicitTracks,
33                         uint32_t aStartFragmentTrack,
34                         uint32_t aEndFragmentTrack,
35                         nsTArray<nscoord>&& aPositions,
36                         nsTArray<nscoord>&& aSizes,
37                         nsTArray<uint32_t>&& aStates,
38                         nsTArray<bool>&& aRemovedRepeatTracks,
39                         uint32_t aRepeatFirstTrack)
40     : mNumLeadingImplicitTracks(aNumLeadingImplicitTracks)
41     , mNumExplicitTracks(aNumExplicitTracks)
42     , mStartFragmentTrack(aStartFragmentTrack)
43     , mEndFragmentTrack(aEndFragmentTrack)
44     , mPositions(aPositions)
45     , mSizes(aSizes)
46     , mStates(aStates)
47     , mRemovedRepeatTracks(aRemovedRepeatTracks)
48     , mRepeatFirstTrack(aRepeatFirstTrack)
49   {}
50   uint32_t mNumLeadingImplicitTracks;
51   uint32_t mNumExplicitTracks;
52   uint32_t mStartFragmentTrack;
53   uint32_t mEndFragmentTrack;
54   nsTArray<nscoord> mPositions;
55   nsTArray<nscoord> mSizes;
56   nsTArray<uint32_t> mStates;
57   nsTArray<bool> mRemovedRepeatTracks;
58   uint32_t mRepeatFirstTrack;
59 };
60 
61 struct ComputedGridLineInfo
62 {
ComputedGridLineInfoComputedGridLineInfo63   explicit ComputedGridLineInfo(nsTArray<nsTArray<nsString>>&& aNames,
64                                 const nsTArray<nsString>& aNamesBefore,
65                                 const nsTArray<nsString>& aNamesAfter)
66     : mNames(aNames)
67     , mNamesBefore(aNamesBefore)
68     , mNamesAfter(aNamesAfter)
69   {}
70   nsTArray<nsTArray<nsString>> mNames;
71   nsTArray<nsString> mNamesBefore;
72   nsTArray<nsString> mNamesAfter;
73 };
74 } // namespace mozilla
75 
76 class nsGridContainerFrame final : public nsContainerFrame
77 {
78 public:
79   NS_DECL_FRAMEARENA_HELPERS
80   NS_DECL_QUERYFRAME_TARGET(nsGridContainerFrame)
81   NS_DECL_QUERYFRAME
82   typedef mozilla::ComputedGridTrackInfo ComputedGridTrackInfo;
83   typedef mozilla::ComputedGridLineInfo ComputedGridLineInfo;
84 
85   // nsIFrame overrides
86   void Reflow(nsPresContext*           aPresContext,
87               ReflowOutput&     aDesiredSize,
88               const ReflowInput& aReflowInput,
89               nsReflowStatus&          aStatus) override;
90   nscoord GetMinISize(nsRenderingContext* aRenderingContext) override;
91   nscoord GetPrefISize(nsRenderingContext* aRenderingContext) override;
92   void MarkIntrinsicISizesDirty() override;
93   nsIAtom* GetType() const override;
IsFrameOfType(uint32_t aFlags)94   bool IsFrameOfType(uint32_t aFlags) const override
95   {
96     return nsContainerFrame::IsFrameOfType(aFlags &
97              ~nsIFrame::eCanContainOverflowContainers);
98   }
99 
100   void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
101                         const nsRect&           aDirtyRect,
102                         const nsDisplayListSet& aLists) override;
103 
GetLogicalBaseline(mozilla::WritingMode aWM)104   nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const override
105   {
106     if (HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) {
107       // Return a baseline synthesized from our margin-box.
108       return nsContainerFrame::GetLogicalBaseline(aWM);
109     }
110     nscoord b;
111     GetBBaseline(BaselineSharingGroup::eFirst, &b);
112     return b;
113   }
114 
GetVerticalAlignBaseline(mozilla::WritingMode aWM,nscoord * aBaseline)115   bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
116                                 nscoord* aBaseline) const override
117   {
118     return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, aBaseline);
119   }
120 
GetNaturalBaselineBOffset(mozilla::WritingMode aWM,BaselineSharingGroup aBaselineGroup,nscoord * aBaseline)121   bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
122                                  BaselineSharingGroup aBaselineGroup,
123                                  nscoord*             aBaseline) const override
124   {
125     if (HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) {
126       return false;
127     }
128     return GetBBaseline(aBaselineGroup, aBaseline);
129   }
130 
131 #ifdef DEBUG_FRAME_DUMP
132   nsresult GetFrameName(nsAString& aResult) const override;
133 #endif
134 
135   // nsContainerFrame overrides
136   bool DrainSelfOverflowList() override;
137   void AppendFrames(ChildListID aListID, nsFrameList& aFrameList) override;
138   void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
139                     nsFrameList& aFrameList) override;
140   void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override;
141   uint16_t CSSAlignmentForAbsPosChild(
142             const ReflowInput& aChildRI,
143             mozilla::LogicalAxis aLogicalAxis) const override;
144 
145 #ifdef DEBUG
146   void SetInitialChildList(ChildListID  aListID,
147                            nsFrameList& aChildList) override;
148 #endif
149 
150   /**
151    * Return the containing block for aChild which MUST be an abs.pos. child
152    * of a grid container.  This is just a helper method for
153    * nsAbsoluteContainingBlock::Reflow - it's not meant to be used elsewhere.
154    */
155   static const nsRect& GridItemCB(nsIFrame* aChild);
156 
NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridItemContainingBlockRect,nsRect)157   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridItemContainingBlockRect, nsRect)
158 
159   /**
160    * These properties are created by a call to
161    * nsGridContainerFrame::GetGridFrameWithComputedInfo, typically from
162    * Element::GetGridFragments.
163    */
164   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridColTrackInfo, ComputedGridTrackInfo)
165   const ComputedGridTrackInfo* GetComputedTemplateColumns()
166   {
167     const ComputedGridTrackInfo* info = Properties().Get(GridColTrackInfo());
168     MOZ_ASSERT(info, "Property generation wasn't requested.");
169     return info;
170   }
171 
NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridRowTrackInfo,ComputedGridTrackInfo)172   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridRowTrackInfo, ComputedGridTrackInfo)
173   const ComputedGridTrackInfo* GetComputedTemplateRows()
174   {
175     const ComputedGridTrackInfo* info = Properties().Get(GridRowTrackInfo());
176     MOZ_ASSERT(info, "Property generation wasn't requested.");
177     return info;
178   }
179 
NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridColumnLineInfo,ComputedGridLineInfo)180   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridColumnLineInfo, ComputedGridLineInfo)
181   const ComputedGridLineInfo* GetComputedTemplateColumnLines()
182   {
183     const ComputedGridLineInfo* info = Properties().Get(GridColumnLineInfo());
184     MOZ_ASSERT(info, "Property generation wasn't requested.");
185     return info;
186   }
187 
NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridRowLineInfo,ComputedGridLineInfo)188   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridRowLineInfo, ComputedGridLineInfo)
189   const ComputedGridLineInfo* GetComputedTemplateRowLines()
190   {
191     const ComputedGridLineInfo* info = Properties().Get(GridRowLineInfo());
192     MOZ_ASSERT(info, "Property generation wasn't requested.");
193     return info;
194   }
195 
196   typedef nsBaseHashtable<nsStringHashKey,
197                           mozilla::css::GridNamedArea,
198                           mozilla::css::GridNamedArea> ImplicitNamedAreas;
NS_DECLARE_FRAME_PROPERTY_DELETABLE(ImplicitNamedAreasProperty,ImplicitNamedAreas)199   NS_DECLARE_FRAME_PROPERTY_DELETABLE(ImplicitNamedAreasProperty,
200                                       ImplicitNamedAreas)
201   ImplicitNamedAreas* GetImplicitNamedAreas() const {
202     return Properties().Get(ImplicitNamedAreasProperty());
203   }
204 
205   typedef nsTArray<mozilla::css::GridNamedArea> ExplicitNamedAreas;
NS_DECLARE_FRAME_PROPERTY_DELETABLE(ExplicitNamedAreasProperty,ExplicitNamedAreas)206   NS_DECLARE_FRAME_PROPERTY_DELETABLE(ExplicitNamedAreasProperty,
207                                       ExplicitNamedAreas)
208   ExplicitNamedAreas* GetExplicitNamedAreas() const {
209     return Properties().Get(ExplicitNamedAreasProperty());
210   }
211 
212   /**
213    * Return a containing grid frame, and ensure it has computed grid info
214    * @return nullptr if aFrame has no grid container, or frame was destroyed
215    * @note this might destroy layout/style data since it may flush layout
216    */
217   static nsGridContainerFrame* GetGridFrameWithComputedInfo(nsIFrame* aFrame);
218 
219   struct TrackSize;
220   struct GridItemInfo;
221   struct GridReflowInput;
222   template<typename Iterator> class GridItemCSSOrderIteratorT;
223   typedef GridItemCSSOrderIteratorT<nsFrameList::iterator>
224     GridItemCSSOrderIterator;
225   typedef GridItemCSSOrderIteratorT<nsFrameList::reverse_iterator>
226     ReverseGridItemCSSOrderIterator;
227   struct FindItemInGridOrderResult
228   {
229     // The first(last) item in (reverse) grid order.
230     const GridItemInfo* mItem;
231     // Does the above item span the first(last) track?
232     bool mIsInEdgeTrack;
233   };
234 protected:
235   static const uint32_t kAutoLine;
236   // The maximum line number, in the zero-based translated grid.
237   static const uint32_t kTranslatedMaxLine;
238   typedef mozilla::LogicalPoint LogicalPoint;
239   typedef mozilla::LogicalRect LogicalRect;
240   typedef mozilla::LogicalSize LogicalSize;
241   typedef mozilla::WritingMode WritingMode;
242   typedef mozilla::css::GridNamedArea GridNamedArea;
243   typedef mozilla::layout::AutoFrameListPtr AutoFrameListPtr;
244   typedef nsLayoutUtils::IntrinsicISizeType IntrinsicISizeType;
245   struct Grid;
246   struct GridArea;
247   class LineNameMap;
248   struct LineRange;
249   struct SharedGridData;
250   struct TrackSizingFunctions;
251   struct Tracks;
252   struct TranslatedLineRange;
253   friend nsContainerFrame* NS_NewGridContainerFrame(nsIPresShell* aPresShell,
254                                                     nsStyleContext* aContext);
nsGridContainerFrame(nsStyleContext * aContext)255   explicit nsGridContainerFrame(nsStyleContext* aContext)
256     : nsContainerFrame(aContext)
257     , mCachedMinISize(NS_INTRINSIC_WIDTH_UNKNOWN)
258     , mCachedPrefISize(NS_INTRINSIC_WIDTH_UNKNOWN)
259   {
260     mBaseline[0][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
261     mBaseline[0][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
262     mBaseline[1][0] = NS_INTRINSIC_WIDTH_UNKNOWN;
263     mBaseline[1][1] = NS_INTRINSIC_WIDTH_UNKNOWN;
264   }
265 
266   /**
267    * XXX temporary - move the ImplicitNamedAreas stuff to the style system.
268    * The implicit area names that come from x-start .. x-end lines in
269    * grid-template-columns / grid-template-rows are stored in this frame
270    * property when needed, as a ImplicitNamedAreas* value.
271    */
272   void InitImplicitNamedAreas(const nsStylePosition* aStyle);
273   void AddImplicitNamedAreas(const nsTArray<nsTArray<nsString>>& aLineNameLists);
274 
275   /**
276    * Reflow and place our children.
277    * @return the consumed size of all of this grid container's continuations
278    *         so far including this frame
279    */
280   nscoord ReflowChildren(GridReflowInput&     aState,
281                          const LogicalRect&   aContentArea,
282                          ReflowOutput& aDesiredSize,
283                          nsReflowStatus&      aStatus);
284 
285   /**
286    * Helper for GetMinISize / GetPrefISize.
287    */
288   nscoord IntrinsicISize(nsRenderingContext* aRenderingContext,
289                          IntrinsicISizeType  aConstraint);
290 
291   // Helper for AppendFrames / InsertFrames.
292   void NoteNewChildren(ChildListID aListID, const nsFrameList& aFrameList);
293 
294   // Helper to move child frames into the kOverflowList.
295   void MergeSortedOverflow(nsFrameList& aList);
296   // Helper to move child frames into the kExcessOverflowContainersList:.
297   void MergeSortedExcessOverflowContainers(nsFrameList& aList);
298 
GetBBaseline(BaselineSharingGroup aBaselineGroup,nscoord * aResult)299   bool GetBBaseline(BaselineSharingGroup aBaselineGroup, nscoord* aResult) const
300   {
301     *aResult = mBaseline[mozilla::eLogicalAxisBlock][aBaselineGroup];
302     return true;
303   }
GetIBaseline(BaselineSharingGroup aBaselineGroup,nscoord * aResult)304   bool GetIBaseline(BaselineSharingGroup aBaselineGroup, nscoord* aResult) const
305   {
306     *aResult = mBaseline[mozilla::eLogicalAxisInline][aBaselineGroup];
307     return true;
308   }
309 
310   /**
311    * Calculate this grid container's baselines.
312    * @param aBaselineSet which baseline(s) to derive from a baseline-group or
313    * items; a baseline not included is synthesized from the border-box instead.
314    * @param aFragmentStartTrack is the first track in this fragment in the same
315    * axis as aMajor.  Pass zero if that's not the axis we're fragmenting in.
316    * @param aFirstExcludedTrack should be the first track in the next fragment
317    * or one beyond the final track in the last fragment, in aMajor's axis.
318    * Pass the number of tracks if that's not the axis we're fragmenting in.
319    */
320   enum BaselineSet : uint32_t {
321     eNone =  0x0,
322     eFirst = 0x1,
323     eLast  = 0x2,
324     eBoth  = eFirst | eLast,
325   };
326   void CalculateBaselines(BaselineSet                   aBaselineSet,
327                           GridItemCSSOrderIterator*     aIter,
328                           const nsTArray<GridItemInfo>* aGridItems,
329                           const Tracks&    aTracks,
330                           uint32_t         aFragmentStartTrack,
331                           uint32_t         aFirstExcludedTrack,
332                           WritingMode      aWM,
333                           const nsSize&    aCBPhysicalSize,
334                           nscoord          aCBBorderPaddingStart,
335                           nscoord          aCBBorderPaddingStartEnd,
336                           nscoord          aCBSize);
337 
338   /**
339    * Synthesize a Grid container baseline for aGroup.
340    */
341   nscoord SynthesizeBaseline(const FindItemInGridOrderResult& aItem,
342                              mozilla::LogicalAxis aAxis,
343                              BaselineSharingGroup aGroup,
344                              const nsSize&        aCBPhysicalSize,
345                              nscoord              aCBSize,
346                              WritingMode          aCBWM);
347   /**
348    * Find the first item in Grid Order in this fragment.
349    * https://drafts.csswg.org/css-grid/#grid-order
350    * @param aFragmentStartTrack is the first track in this fragment in the same
351    * axis as aMajor.  Pass zero if that's not the axis we're fragmenting in.
352    */
353   static FindItemInGridOrderResult
354   FindFirstItemInGridOrder(GridItemCSSOrderIterator& aIter,
355                            const nsTArray<GridItemInfo>& aGridItems,
356                            LineRange GridArea::* aMajor,
357                            LineRange GridArea::* aMinor,
358                            uint32_t aFragmentStartTrack);
359   /**
360    * Find the last item in Grid Order in this fragment.
361    * @param aFragmentStartTrack is the first track in this fragment in the same
362    * axis as aMajor.  Pass zero if that's not the axis we're fragmenting in.
363    * @param aFirstExcludedTrack should be the first track in the next fragment
364    * or one beyond the final track in the last fragment, in aMajor's axis.
365    * Pass the number of tracks if that's not the axis we're fragmenting in.
366    */
367   static FindItemInGridOrderResult
368   FindLastItemInGridOrder(ReverseGridItemCSSOrderIterator& aIter,
369                           const nsTArray<GridItemInfo>& aGridItems,
370                           LineRange GridArea::* aMajor,
371                           LineRange GridArea::* aMinor,
372                           uint32_t aFragmentStartTrack,
373                           uint32_t aFirstExcludedTrack);
374 
375 #ifdef DEBUG
376   void SanityCheckGridItemsBeforeReflow() const;
377 #endif // DEBUG
378 
379 private:
380   // Helpers for ReflowChildren
381   struct Fragmentainer {
382     /**
383      * The distance from the first grid container fragment's block-axis content
384      * edge to the fragmentainer end.
385      */
386     nscoord mToFragmentainerEnd;
387     /**
388      * True if the current fragment is at the start of the fragmentainer.
389      */
390     bool mIsTopOfPage;
391     /**
392      * Is there a Class C break opportunity at the start content edge?
393      */
394     bool mCanBreakAtStart;
395     /**
396      * Is there a Class C break opportunity at the end content edge?
397      */
398     bool mCanBreakAtEnd;
399     /**
400      * Is the grid container's block-size unconstrained?
401      */
402     bool mIsAutoBSize;
403   };
404 
405   mozilla::Maybe<nsGridContainerFrame::Fragmentainer>
406     GetNearestFragmentainer(const GridReflowInput& aState) const;
407 
408   // @return the consumed size of all continuations so far including this frame
409   nscoord ReflowInFragmentainer(GridReflowInput&     aState,
410                                 const LogicalRect&   aContentArea,
411                                 ReflowOutput& aDesiredSize,
412                                 nsReflowStatus&      aStatus,
413                                 Fragmentainer&       aFragmentainer,
414                                 const nsSize&        aContainerSize);
415 
416   // Helper for ReflowInFragmentainer
417   // @return the consumed size of all continuations so far including this frame
418   nscoord ReflowRowsInFragmentainer(GridReflowInput&     aState,
419                                     const LogicalRect&   aContentArea,
420                                     ReflowOutput& aDesiredSize,
421                                     nsReflowStatus&      aStatus,
422                                     Fragmentainer&       aFragmentainer,
423                                     const nsSize&        aContainerSize,
424                                     const nsTArray<const GridItemInfo*>& aItems,
425                                     uint32_t             aStartRow,
426                                     uint32_t             aEndRow,
427                                     nscoord              aBSize,
428                                     nscoord              aAvailableSize);
429 
430   // Helper for ReflowChildren / ReflowInFragmentainer
431   void ReflowInFlowChild(nsIFrame*               aChild,
432                          const GridItemInfo*     aGridItemInfo,
433                          nsSize                  aContainerSize,
434                          mozilla::Maybe<nscoord> aStretchBSize,
435                          const Fragmentainer*    aFragmentainer,
436                          const GridReflowInput&  aState,
437                          const LogicalRect&      aContentArea,
438                          ReflowOutput&    aDesiredSize,
439                          nsReflowStatus&         aStatus);
440 
441   /**
442    * Cached values to optimize GetMinISize/GetPrefISize.
443    */
444   nscoord mCachedMinISize;
445   nscoord mCachedPrefISize;
446 
447   // Our baselines, one per BaselineSharingGroup per axis.
448   nscoord mBaseline[2/*LogicalAxis*/][2/*BaselineSharingGroup*/];
449 
450 #ifdef DEBUG
451   // If true, NS_STATE_GRID_DID_PUSH_ITEMS may be set even though all pushed
452   // frames may have been removed.  This is used to suppress an assertion
453   // in case RemoveFrame removed all associated child frames.
454   bool mDidPushItemsBitMayLie;
455 #endif
456 };
457 
458 #endif /* nsGridContainerFrame_h___ */
459