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