[css-grid] Refactoring to make more explicit the orthogonal items' pre-layout logic
[WebKit-https.git] / Source / WebCore / rendering / GridTrackSizingAlgorithm.h
1 /*
2  * Copyright (C) 2017 Igalia S.L.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 #pragma once
26
27 #include "Grid.h"
28 #include "GridTrackSize.h"
29 #include "LayoutUnit.h"
30
31 namespace WebCore {
32
33 static const int infinity = -1;
34
35 enum SizingOperation { TrackSizing, IntrinsicSizeComputation };
36
37 enum TrackSizeComputationPhase {
38     ResolveIntrinsicMinimums,
39     ResolveContentBasedMinimums,
40     ResolveMaxContentMinimums,
41     ResolveIntrinsicMaximums,
42     ResolveMaxContentMaximums,
43     MaximizeTracks,
44 };
45
46 class GridTrackSizingAlgorithmStrategy;
47
48 class GridTrack {
49 public:
50     GridTrack() = default;
51
52     const LayoutUnit& baseSize() const;
53     void setBaseSize(LayoutUnit);
54
55     const LayoutUnit& growthLimit() const;
56     bool growthLimitIsInfinite() const { return m_growthLimit == infinity; }
57     void setGrowthLimit(LayoutUnit);
58
59     bool infiniteGrowthPotential() const { return growthLimitIsInfinite() || m_infinitelyGrowable; }
60     const LayoutUnit& growthLimitIfNotInfinite() const;
61
62     const LayoutUnit& plannedSize() const { return m_plannedSize; }
63     void setPlannedSize(LayoutUnit plannedSize) { m_plannedSize = plannedSize; }
64
65     const LayoutUnit& tempSize() const { return m_tempSize; }
66     void setTempSize(const LayoutUnit&);
67     void growTempSize(const LayoutUnit&);
68
69     bool infinitelyGrowable() const { return m_infinitelyGrowable; }
70     void setInfinitelyGrowable(bool infinitelyGrowable) { m_infinitelyGrowable = infinitelyGrowable; }
71
72     void setGrowthLimitCap(std::optional<LayoutUnit>);
73     std::optional<LayoutUnit> growthLimitCap() const { return m_growthLimitCap; }
74
75 private:
76     bool isGrowthLimitBiggerThanBaseSize() const { return growthLimitIsInfinite() || m_growthLimit >= m_baseSize; }
77
78     void ensureGrowthLimitIsBiggerThanBaseSize();
79
80     LayoutUnit m_baseSize { 0 };
81     LayoutUnit m_growthLimit { 0 };
82     LayoutUnit m_plannedSize { 0 };
83     LayoutUnit m_tempSize { 0 };
84     std::optional<LayoutUnit> m_growthLimitCap;
85     bool m_infinitelyGrowable { false };
86 };
87
88 class GridTrackSizingAlgorithm final {
89     friend class GridTrackSizingAlgorithmStrategy;
90
91 public:
92     GridTrackSizingAlgorithm(const RenderGrid* renderGrid, Grid& grid)
93         : m_grid(grid)
94         , m_renderGrid(renderGrid)
95         , m_sizingState(ColumnSizingFirstIteration)
96     {
97     }
98
99     void setup(GridTrackSizingDirection, unsigned numTracks, SizingOperation, std::optional<LayoutUnit> availableSpace, std::optional<LayoutUnit> freeSpace);
100     void run();
101     void reset();
102
103     // Required by RenderGrid. Try to minimize the exposed surface.
104     const Grid& grid() const { return m_grid; }
105     // FIXME (jfernandez): We should remove any public getter for this attribute
106     // and encapsulate any access in the algorithm class.
107     Grid& mutableGrid() const { return m_grid; }
108
109     LayoutUnit minContentSize() const { return m_minContentSize; };
110     LayoutUnit maxContentSize() const { return m_maxContentSize; };
111
112     LayoutSize estimatedGridAreaBreadthForChild(const RenderBox&) const;
113
114     Vector<GridTrack>& tracks(GridTrackSizingDirection direction) { return direction == ForColumns ? m_columns : m_rows; }
115     const Vector<GridTrack>& tracks(GridTrackSizingDirection direction) const { return direction == ForColumns ? m_columns : m_rows; }
116
117     std::optional<LayoutUnit> freeSpace(GridTrackSizingDirection direction) const { return direction == ForColumns ? m_freeSpaceColumns : m_freeSpaceRows; }
118     void setFreeSpace(GridTrackSizingDirection, std::optional<LayoutUnit>);
119
120     std::optional<LayoutUnit> availableSpace(GridTrackSizingDirection direction) const { return direction == ForColumns ? m_availableSpaceColumns : m_availableSpaceRows; }
121     void setAvailableSpace(GridTrackSizingDirection, std::optional<LayoutUnit>);
122
123     LayoutUnit computeTrackBasedSize() const;
124
125     bool hasAnyPercentSizedRowsIndefiniteHeight() const { return m_hasPercentSizedRowsIndefiniteHeight; }
126
127 #ifndef NDEBUG
128     bool tracksAreWiderThanMinTrackBreadth() const;
129 #endif
130
131 private:
132     std::optional<LayoutUnit> availableSpace() const { return availableSpace(m_direction); }
133     bool isRelativeGridLengthAsAuto(const GridLength&, GridTrackSizingDirection) const;
134     GridTrackSize gridTrackSize(GridTrackSizingDirection, unsigned translatedIndex) const;
135     const GridTrackSize& rawGridTrackSize(GridTrackSizingDirection, unsigned translatedIndex) const;
136
137     // Helper methods for step 1. initializeTrackSizes().
138     LayoutUnit initialBaseSize(const GridTrackSize&) const;
139     LayoutUnit initialGrowthLimit(const GridTrackSize&, LayoutUnit baseSize) const;
140
141     // Helper methods for step 2. resolveIntrinsicTrackSizes().
142     void sizeTrackToFitNonSpanningItem(const GridSpan&, RenderBox& gridItem, GridTrack&);
143     bool spanningItemCrossesFlexibleSizedTracks(const GridSpan&) const;
144     typedef struct GridItemsSpanGroupRange GridItemsSpanGroupRange;
145     template <TrackSizeComputationPhase phase> void increaseSizesToAccommodateSpanningItems(const GridItemsSpanGroupRange& gridItemsWithSpan);
146     LayoutUnit itemSizeForTrackSizeComputationPhase(TrackSizeComputationPhase, RenderBox&) const;
147     template <TrackSizeComputationPhase phase> void distributeSpaceToTracks(Vector<GridTrack*>& tracks, Vector<GridTrack*>* growBeyondGrowthLimitsTracks, LayoutUnit& availableLogicalSpace) const;
148     LayoutUnit estimatedGridAreaBreadthForChild(const RenderBox&, GridTrackSizingDirection) const;
149     LayoutUnit gridAreaBreadthForChild(const RenderBox&, GridTrackSizingDirection) const;
150
151     void computeGridContainerIntrinsicSizes();
152
153     // Helper methods for step 4. Strech flexible tracks.
154     typedef HashSet<unsigned, DefaultHash<unsigned>::Hash, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> TrackIndexSet;
155     double computeFlexFactorUnitSize(const Vector<GridTrack>& tracks, double flexFactorSum, LayoutUnit& leftOverSpace, const Vector<unsigned, 8>& flexibleTracksIndexes, std::unique_ptr<TrackIndexSet> tracksToTreatAsInflexible = nullptr) const;
156     void computeFlexSizedTracksGrowth(double flexFraction, Vector<LayoutUnit>& increments, LayoutUnit& totalGrowth) const;
157     double findFrUnitSize(const GridSpan& tracksSpan, LayoutUnit leftOverSpace) const;
158
159     // Track sizing algorithm steps. Note that the "Maximize Tracks" step is done
160     // entirely inside the strategies, that's why we don't need an additional
161     // method at thise level.
162     void initializeTrackSizes();
163     void resolveIntrinsicTrackSizes();
164     void stretchFlexibleTracks(std::optional<LayoutUnit> freeSpace);
165     void stretchAutoTracks();
166
167     // State machine.
168     void advanceNextState();
169     bool isValidTransition() const;
170
171     // Data.
172     bool wasSetup() const { return !!m_strategy; }
173     bool m_needsSetup { true };
174     bool m_hasPercentSizedRowsIndefiniteHeight { false };
175     std::optional<LayoutUnit> m_availableSpaceRows;
176     std::optional<LayoutUnit> m_availableSpaceColumns;
177
178     std::optional<LayoutUnit> m_freeSpaceColumns;
179     std::optional<LayoutUnit> m_freeSpaceRows;
180
181     // We need to keep both alive in order to properly size grids with orthogonal
182     // writing modes.
183     Vector<GridTrack> m_columns;
184     Vector<GridTrack> m_rows;
185     Vector<unsigned> m_contentSizedTracksIndex;
186     Vector<unsigned> m_flexibleSizedTracksIndex;
187     Vector<unsigned> m_autoSizedTracksForStretchIndex;
188
189     GridTrackSizingDirection m_direction;
190     SizingOperation m_sizingOperation;
191
192     Grid& m_grid;
193
194     const RenderGrid* m_renderGrid;
195     std::unique_ptr<GridTrackSizingAlgorithmStrategy> m_strategy;
196
197     // The track sizing algorithm is used for both layout and intrinsic size
198     // computation. We're normally just interested in intrinsic inline sizes
199     // (a.k.a widths in most of the cases) for the computeIntrinsicLogicalWidths()
200     // computations. That's why we don't need to keep around different values for
201     // rows/columns.
202     LayoutUnit m_minContentSize;
203     LayoutUnit m_maxContentSize;
204
205     enum SizingState {
206         ColumnSizingFirstIteration,
207         RowSizingFirstIteration,
208         ColumnSizingSecondIteration,
209         RowSizingSecondIteration
210     };
211     SizingState m_sizingState;
212
213     // This is a RAII class used to ensure that the track sizing algorithm is
214     // executed as it is suppossed to be, i.e., first resolve columns and then
215     // rows. Only if required a second iteration is run following the same order,
216     // first columns and then rows.
217     class StateMachine {
218     public:
219         StateMachine(GridTrackSizingAlgorithm&);
220         ~StateMachine();
221
222     private:
223         GridTrackSizingAlgorithm& m_algorithm;
224     };
225 };
226
227 class GridTrackSizingAlgorithmStrategy {
228 public:
229     LayoutUnit minContentForChild(RenderBox&) const;
230     LayoutUnit maxContentForChild(RenderBox&) const;
231     LayoutUnit minSizeForChild(RenderBox&) const;
232
233     virtual ~GridTrackSizingAlgorithmStrategy() = default;
234
235     virtual void maximizeTracks(Vector<GridTrack>&, std::optional<LayoutUnit>& freeSpace) = 0;
236     virtual double findUsedFlexFraction(Vector<unsigned>& flexibleSizedTracksIndex, GridTrackSizingDirection, std::optional<LayoutUnit> initialFreeSpace) const = 0;
237     virtual bool recomputeUsedFlexFractionIfNeeded(double& flexFraction, LayoutUnit& totalGrowth) const = 0;
238     virtual LayoutUnit freeSpaceForStretchAutoTracksStep() const = 0;
239
240 protected:
241     GridTrackSizingAlgorithmStrategy(GridTrackSizingAlgorithm& algorithm)
242         : m_algorithm(algorithm) { }
243
244     virtual LayoutUnit minLogicalWidthForChild(RenderBox&, Length childMinSize, LayoutUnit availableSize) const = 0;
245     virtual void layoutGridItemForMinSizeComputation(RenderBox&, bool overrideSizeHasChanged) const = 0;
246
247     LayoutUnit logicalHeightForChild(RenderBox&) const;
248     bool updateOverrideContainingBlockContentSizeForChild(RenderBox&, GridTrackSizingDirection, std::optional<LayoutUnit> = std::nullopt) const;
249
250     GridTrackSize gridTrackSize(GridTrackSizingDirection direction, size_t translatedIndex) const { return m_algorithm.gridTrackSize(direction, translatedIndex); }
251
252     // GridTrackSizingAlgorithm accessors for subclasses.
253     LayoutUnit computeTrackBasedSize() const { return m_algorithm.computeTrackBasedSize(); }
254     GridTrackSizingDirection direction() const { return m_algorithm.m_direction; }
255     double findFrUnitSize(const GridSpan& tracksSpan, LayoutUnit leftOverSpace) const { return m_algorithm.findFrUnitSize(tracksSpan, leftOverSpace); }
256     void distributeSpaceToTracks(Vector<GridTrack*>& tracks, LayoutUnit& availableLogicalSpace) const { m_algorithm.distributeSpaceToTracks<MaximizeTracks>(tracks, nullptr, availableLogicalSpace); }
257     const RenderGrid* renderGrid() const { return m_algorithm.m_renderGrid; }
258     std::optional<LayoutUnit> availableSpace() const { return m_algorithm.availableSpace(); }
259
260     GridTrackSizingAlgorithm& m_algorithm;
261 };
262
263 } // namespace WebCore