summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.h
blob: c2f67decf4f43a192672eb8427f88f681333aacc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_GRID_TRACK_SIZING_ALGORITHM_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_GRID_TRACK_SIZING_ALGORITHM_H_

#include <memory>
#include "base/macros.h"
#include "base/optional.h"
#include "third_party/blink/renderer/core/layout/grid_baseline_alignment.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/core/style/grid_positions_resolver.h"
#include "third_party/blink/renderer/core/style/grid_track_size.h"
#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"

namespace blink {

static const int kInfinity = -1;

class Grid;
class GridTrackSizingAlgorithmStrategy;
class LayoutGrid;

enum TrackSizeComputationPhase {
  kResolveIntrinsicMinimums,
  kResolveContentBasedMinimums,
  kResolveMaxContentMinimums,
  kResolveIntrinsicMaximums,
  kResolveMaxContentMaximums,
  kMaximizeTracks,
};

class GridTrack {
  DISALLOW_NEW();

 public:
  GridTrack() : infinitely_growable_(false) {}

  LayoutUnit BaseSize() const;
  void SetBaseSize(LayoutUnit);

  LayoutUnit GrowthLimit() const;
  bool GrowthLimitIsInfinite() const { return growth_limit_ == kInfinity; }
  void SetGrowthLimit(LayoutUnit);

  bool InfiniteGrowthPotential() const;

  LayoutUnit PlannedSize() const { return planned_size_; }
  void SetPlannedSize(LayoutUnit);

  LayoutUnit SizeDuringDistribution() const {
    return size_during_distribution_;
  }
  void SetSizeDuringDistribution(LayoutUnit);

  void GrowSizeDuringDistribution(LayoutUnit);

  bool InfinitelyGrowable() const { return infinitely_growable_; }
  void SetInfinitelyGrowable(bool);

  base::Optional<LayoutUnit> GrowthLimitCap() const {
    return growth_limit_cap_;
  }
  void SetGrowthLimitCap(base::Optional<LayoutUnit>);

  const GridTrackSize& CachedTrackSize() const {
    DCHECK(cached_track_size_.has_value());
    return cached_track_size_.value();
  }
  void SetCachedTrackSize(const GridTrackSize&);

 private:
  bool IsGrowthLimitBiggerThanBaseSize() const;
  void EnsureGrowthLimitIsBiggerThanBaseSize();

  LayoutUnit base_size_;
  LayoutUnit growth_limit_;
  LayoutUnit planned_size_;
  LayoutUnit size_during_distribution_;
  base::Optional<LayoutUnit> growth_limit_cap_;
  bool infinitely_growable_;
  base::Optional<GridTrackSize> cached_track_size_;
};

class GridTrackSizingAlgorithm final {
  friend class GridTrackSizingAlgorithmStrategy;

 public:
  GridTrackSizingAlgorithm(const LayoutGrid* layout_grid, Grid& grid)
      : grid_(grid),
        layout_grid_(layout_grid),
        sizing_state_(kColumnSizingFirstIteration) {}

  // Setup() must be run before calling Run() as it configures the behaviour of
  // the algorithm.
  void Setup(GridTrackSizingDirection,
             size_t num_tracks,
             base::Optional<LayoutUnit> available_space);
  void Run();
  void Reset();

  // Required by LayoutGrid. Try to minimize the exposed surface.
  const Grid& GetGrid() const { return grid_; }
  // TODO (jfernandez): We should remove any public getter for this attribute
  // and encapsulate any access in the algorithm class.
  Grid& GetMutableGrid() const { return grid_; }
  LayoutUnit MinContentSize() const { return min_content_size_; }
  LayoutUnit MaxContentSize() const { return max_content_size_; }

  LayoutUnit BaselineOffsetForChild(const LayoutBox&, GridAxis) const;

  void CacheBaselineAlignedItem(const LayoutBox&, GridAxis);
  void CopyBaselineItemsCache(const GridTrackSizingAlgorithm&, GridAxis);
  void ClearBaselineItemsCache();

  LayoutSize EstimatedGridAreaBreadthForChild(const LayoutBox& child) const;

  Vector<GridTrack>& Tracks(GridTrackSizingDirection);
  const Vector<GridTrack>& Tracks(GridTrackSizingDirection) const;

  base::Optional<LayoutUnit> FreeSpace(GridTrackSizingDirection) const;
  void SetFreeSpace(GridTrackSizingDirection, base::Optional<LayoutUnit>);

  base::Optional<LayoutUnit> AvailableSpace(GridTrackSizingDirection) const;
  void SetAvailableSpace(GridTrackSizingDirection, base::Optional<LayoutUnit>);

#if DCHECK_IS_ON()
  bool TracksAreWiderThanMinTrackBreadth() const;
#endif

  LayoutUnit ComputeTrackBasedSize() const;

  bool HasAnyPercentSizedRowsIndefiniteHeight() const {
    return has_percent_sized_rows_indefinite_height_;
  }

 private:
  base::Optional<LayoutUnit> AvailableSpace() const;
  bool IsRelativeGridLengthAsAuto(const GridLength&,
                                  GridTrackSizingDirection) const;
  bool IsRelativeSizedTrackAsAuto(const GridTrackSize&,
                                  GridTrackSizingDirection) const;
  GridTrackSize CalculateGridTrackSize(GridTrackSizingDirection,
                                       size_t translated_index) const;
  const GridTrackSize& RawGridTrackSize(GridTrackSizingDirection,
                                        size_t translated_index) const;

  // Helper methods for step 1. initializeTrackSizes().
  LayoutUnit InitialBaseSize(const GridTrackSize&) const;
  LayoutUnit InitialGrowthLimit(const GridTrackSize&,
                                LayoutUnit base_size) const;

  // Helper methods for step 2. resolveIntrinsicTrackSizes().
  void SizeTrackToFitNonSpanningItem(const GridSpan&,
                                     LayoutBox& grid_item,
                                     GridTrack&);
  bool SpanningItemCrossesFlexibleSizedTracks(const GridSpan&) const;
  typedef struct GridItemsSpanGroupRange GridItemsSpanGroupRange;
  template <TrackSizeComputationPhase phase>
  void IncreaseSizesToAccommodateSpanningItems(
      const GridItemsSpanGroupRange& grid_items_with_span);
  LayoutUnit ItemSizeForTrackSizeComputationPhase(TrackSizeComputationPhase,
                                                  LayoutBox&) const;
  template <TrackSizeComputationPhase phase>
  void DistributeSpaceToTracks(
      Vector<GridTrack*>& tracks,
      Vector<GridTrack*>* grow_beyond_growth_limits_tracks,
      LayoutUnit& available_logical_space) const;
  LayoutUnit EstimatedGridAreaBreadthForChild(const LayoutBox&,
                                              GridTrackSizingDirection) const;
  LayoutUnit GridAreaBreadthForChild(const LayoutBox&,
                                     GridTrackSizingDirection) const;

  void ComputeBaselineAlignmentContext();
  void UpdateBaselineAlignmentContext(const LayoutBox&, GridAxis);
  bool CanParticipateInBaselineAlignment(const LayoutBox&, GridAxis) const;
  bool ParticipateInBaselineAlignment(const LayoutBox&, GridAxis) const;

  bool IsIntrinsicSizedGridArea(const LayoutBox&, GridAxis) const;
  void ComputeGridContainerIntrinsicSizes();

  // Helper methods for step 4. Strech flexible tracks.
  typedef HashSet<size_t,
                  DefaultHash<size_t>::Hash,
                  WTF::UnsignedWithZeroKeyHashTraits<size_t>>
      TrackIndexSet;
  double ComputeFlexFactorUnitSize(
      const Vector<GridTrack>& tracks,
      double flex_factor_sum,
      LayoutUnit& left_over_space,
      const Vector<size_t, 8>& flexible_tracks_indexes,
      std::unique_ptr<TrackIndexSet> tracks_to_treat_as_inflexible =
          nullptr) const;
  void ComputeFlexSizedTracksGrowth(double flex_fraction,
                                    Vector<LayoutUnit>& increments,
                                    LayoutUnit& total_growth) const;
  double FindFrUnitSize(const GridSpan& tracks_span,
                        LayoutUnit left_over_space) const;

  // Track sizing algorithm steps. Note that the "Maximize Tracks" step is done
  // entirely inside the strategies, that's why we don't need an additional
  // method at thise level.
  void InitializeTrackSizes();
  void ResolveIntrinsicTrackSizes();
  void StretchFlexibleTracks(base::Optional<LayoutUnit> free_space);
  void StretchAutoTracks();

  // State machine.
  void AdvanceNextState();
  bool IsValidTransition() const;

  // Data.
  bool WasSetup() const { return !!strategy_; }
  bool needs_setup_{true};
  bool has_percent_sized_rows_indefinite_height_{false};
  base::Optional<LayoutUnit> available_space_columns_;
  base::Optional<LayoutUnit> available_space_rows_;

  base::Optional<LayoutUnit> free_space_columns_;
  base::Optional<LayoutUnit> free_space_rows_;

  // We need to keep both alive in order to properly size grids with orthogonal
  // writing modes.
  Vector<GridTrack> columns_;
  Vector<GridTrack> rows_;
  Vector<size_t> content_sized_tracks_index_;
  Vector<size_t> flexible_sized_tracks_index_;
  Vector<size_t> auto_sized_tracks_for_stretch_index_;

  GridTrackSizingDirection direction_;

  Grid& grid_;

  const LayoutGrid* layout_grid_;
  std::unique_ptr<GridTrackSizingAlgorithmStrategy> strategy_;

  // The track sizing algorithm is used for both layout and intrinsic size
  // computation. We're normally just interested in intrinsic inline sizes
  // (a.k.a widths in most of the cases) for the computeIntrinsicLogicalWidths()
  // computations. That's why we don't need to keep around different values for
  // rows/columns.
  LayoutUnit min_content_size_;
  LayoutUnit max_content_size_;

  enum SizingState {
    kColumnSizingFirstIteration,
    kRowSizingFirstIteration,
    kColumnSizingSecondIteration,
    kRowSizingSecondIteration
  };
  SizingState sizing_state_;

  GridBaselineAlignment baseline_alignment_;
  typedef HashMap<const LayoutBox*, bool> BaselineItemsCache;
  BaselineItemsCache column_baseline_items_map_;
  BaselineItemsCache row_baseline_items_map_;

  // This is a RAII class used to ensure that the track sizing algorithm is
  // executed as it is suppossed to be, i.e., first resolve columns and then
  // rows. Only if required a second iteration is run following the same order,
  // first columns and then rows.
  class StateMachine {
   public:
    StateMachine(GridTrackSizingAlgorithm&);
    ~StateMachine();

   private:
    GridTrackSizingAlgorithm& algorithm_;
  };
};

class GridTrackSizingAlgorithmStrategy {
  USING_FAST_MALLOC(GridTrackSizingAlgorithmStrategy);

 public:
  virtual ~GridTrackSizingAlgorithmStrategy();

  virtual LayoutUnit MinContentForChild(LayoutBox&) const;
  virtual LayoutUnit MaxContentForChild(LayoutBox&) const;
  LayoutUnit MinSizeForChild(LayoutBox&) const;

  virtual void MaximizeTracks(Vector<GridTrack>&,
                              base::Optional<LayoutUnit>& free_space) = 0;
  virtual double FindUsedFlexFraction(
      Vector<size_t>& flexible_sized_tracks_index,
      GridTrackSizingDirection,
      base::Optional<LayoutUnit> initial_free_space) const = 0;
  virtual bool RecomputeUsedFlexFractionIfNeeded(
      Vector<size_t>& flexible_sized_tracks_index,
      double& flex_fraction,
      Vector<LayoutUnit>& increments,
      LayoutUnit& total_growth) const = 0;
  virtual LayoutUnit FreeSpaceForStretchAutoTracksStep() const = 0;
  virtual bool IsComputingSizeContainment() const = 0;

 protected:
  GridTrackSizingAlgorithmStrategy(GridTrackSizingAlgorithm& algorithm)
      : algorithm_(algorithm) {}

  virtual LayoutUnit MinLogicalSizeForChild(LayoutBox&,
                                            const Length& child_min_size,
                                            LayoutUnit available_size) const;
  virtual void LayoutGridItemForMinSizeComputation(
      LayoutBox&,
      bool override_size_has_changed) const = 0;

  LayoutUnit LogicalHeightForChild(LayoutBox&) const;

  bool UpdateOverrideContainingBlockContentSizeForChild(
      LayoutBox&,
      GridTrackSizingDirection,
      base::Optional<LayoutUnit> = base::nullopt) const;
  LayoutUnit ComputeTrackBasedSize() const;

  GridTrackSizingDirection Direction() const { return algorithm_.direction_; }
  double FindFrUnitSize(const GridSpan& tracks_span,
                        LayoutUnit left_over_space) const;
  void DistributeSpaceToTracks(Vector<GridTrack*>& tracks,
                               LayoutUnit& available_logical_space) const;
  const LayoutGrid* GetLayoutGrid() const { return algorithm_.layout_grid_; }
  base::Optional<LayoutUnit> AvailableSpace() const {
    return algorithm_.AvailableSpace();
  }

  // Helper functions
  static bool ShouldClearOverrideContainingBlockContentSizeForChild(
      const LayoutGrid&,
      const LayoutBox& child,
      GridTrackSizingDirection);
  static void SetOverrideContainingBlockContentSizeForChild(
      LayoutBox& child,
      GridTrackSizingDirection,
      LayoutUnit size);

  GridTrackSizingAlgorithm& algorithm_;

 private:
  DISALLOW_COPY_AND_ASSIGN(GridTrackSizingAlgorithmStrategy);
};
}

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_GRID_TRACK_SIZING_ALGORITHM_H_