1 /*
2 * Copyright (C) 2011 Apple Inc. All rights reserved.
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 COMPUTER, 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
26 #include "third_party/blink/renderer/core/layout/layout_grid.h"
27
28 #include <algorithm>
29 #include <memory>
30 #include <utility>
31
32 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
33 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
34 #include "third_party/blink/renderer/core/layout/grid_layout_utils.h"
35 #include "third_party/blink/renderer/core/layout/layout_state.h"
36 #include "third_party/blink/renderer/core/layout/text_autosizer.h"
37 #include "third_party/blink/renderer/core/paint/block_painter.h"
38 #include "third_party/blink/renderer/core/paint/paint_layer.h"
39 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
40 #include "third_party/blink/renderer/core/style/computed_style.h"
41 #include "third_party/blink/renderer/core/style/grid_area.h"
42 #include "third_party/blink/renderer/platform/geometry/length_functions.h"
43 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
44 #include "third_party/blink/renderer/platform/text/writing_mode.h"
45
46 namespace blink {
47
LayoutGrid(Element * element)48 LayoutGrid::LayoutGrid(Element* element)
49 : LayoutBlock(element),
50 grid_(Grid::Create(this)),
51 track_sizing_algorithm_(this, *grid_) {
52 DCHECK(!ChildrenInline());
53 }
54
55 LayoutGrid::~LayoutGrid() = default;
56
CreateAnonymous(Document * document)57 LayoutGrid* LayoutGrid::CreateAnonymous(Document* document) {
58 LayoutGrid* layout_grid = new LayoutGrid(nullptr);
59 layout_grid->SetDocumentForAnonymous(document);
60 return layout_grid;
61 }
62
AddChild(LayoutObject * new_child,LayoutObject * before_child)63 void LayoutGrid::AddChild(LayoutObject* new_child, LayoutObject* before_child) {
64 NOT_DESTROYED();
65 LayoutBlock::AddChild(new_child, before_child);
66
67 // Positioned grid items do not take up space or otherwise participate in the
68 // layout of the grid, for that reason we don't need to mark the grid as dirty
69 // when they are added.
70 if (new_child->IsOutOfFlowPositioned())
71 return;
72
73 // The grid needs to be recomputed as it might contain auto-placed items that
74 // will change their position.
75 DirtyGrid();
76 }
77
RemoveChild(LayoutObject * child)78 void LayoutGrid::RemoveChild(LayoutObject* child) {
79 NOT_DESTROYED();
80 LayoutBlock::RemoveChild(child);
81
82 // Positioned grid items do not take up space or otherwise participate in the
83 // layout of the grid, for that reason we don't need to mark the grid as dirty
84 // when they are removed.
85 if (child->IsOutOfFlowPositioned())
86 return;
87
88 // The grid needs to be recomputed as it might contain auto-placed items that
89 // will change their position.
90 DirtyGrid();
91 }
92
SelfAlignmentForChild(GridAxis axis,const LayoutBox & child,const ComputedStyle * style) const93 StyleSelfAlignmentData LayoutGrid::SelfAlignmentForChild(
94 GridAxis axis,
95 const LayoutBox& child,
96 const ComputedStyle* style) const {
97 NOT_DESTROYED();
98 return axis == kGridRowAxis ? JustifySelfForChild(child, style)
99 : AlignSelfForChild(child, style);
100 }
101
DefaultAlignment(GridAxis axis,const ComputedStyle & style) const102 StyleSelfAlignmentData LayoutGrid::DefaultAlignment(
103 GridAxis axis,
104 const ComputedStyle& style) const {
105 NOT_DESTROYED();
106 return axis == kGridRowAxis
107 ? style.ResolvedJustifyItems(ItemPosition::kNormal)
108 : style.ResolvedAlignItems(ItemPosition::kNormal);
109 }
110
DefaultAlignmentIsStretchOrNormal(GridAxis axis,const ComputedStyle & style) const111 bool LayoutGrid::DefaultAlignmentIsStretchOrNormal(
112 GridAxis axis,
113 const ComputedStyle& style) const {
114 NOT_DESTROYED();
115 ItemPosition alignment = DefaultAlignment(axis, style).GetPosition();
116 return alignment == ItemPosition::kStretch ||
117 alignment == ItemPosition::kNormal;
118 }
119
SelfAlignmentChangedSize(GridAxis axis,const ComputedStyle & old_style,const ComputedStyle & new_style,const LayoutBox & child) const120 bool LayoutGrid::SelfAlignmentChangedSize(GridAxis axis,
121 const ComputedStyle& old_style,
122 const ComputedStyle& new_style,
123 const LayoutBox& child) const {
124 NOT_DESTROYED();
125 return SelfAlignmentForChild(axis, child, &old_style).GetPosition() ==
126 ItemPosition::kStretch
127 ? SelfAlignmentForChild(axis, child, &new_style).GetPosition() !=
128 ItemPosition::kStretch
129 : SelfAlignmentForChild(axis, child, &new_style).GetPosition() ==
130 ItemPosition::kStretch;
131 }
132
DefaultAlignmentChangedSize(GridAxis axis,const ComputedStyle & old_style,const ComputedStyle & new_style) const133 bool LayoutGrid::DefaultAlignmentChangedSize(
134 GridAxis axis,
135 const ComputedStyle& old_style,
136 const ComputedStyle& new_style) const {
137 NOT_DESTROYED();
138 return DefaultAlignmentIsStretchOrNormal(axis, old_style)
139 ? DefaultAlignment(axis, old_style).GetPosition() !=
140 DefaultAlignment(axis, new_style).GetPosition()
141 : DefaultAlignmentIsStretchOrNormal(axis, new_style);
142 }
143
StyleDidChange(StyleDifference diff,const ComputedStyle * old_style)144 void LayoutGrid::StyleDidChange(StyleDifference diff,
145 const ComputedStyle* old_style) {
146 NOT_DESTROYED();
147 LayoutBlock::StyleDidChange(diff, old_style);
148 if (!old_style)
149 return;
150
151 const ComputedStyle& new_style = StyleRef();
152 if (diff.NeedsFullLayout() &&
153 (DefaultAlignmentChangedSize(kGridRowAxis, *old_style, new_style) ||
154 DefaultAlignmentChangedSize(kGridColumnAxis, *old_style, new_style))) {
155 // Style changes on the grid container implying stretching (to-stretch) or
156 // shrinking (from-stretch) require the affected items to be laid out again.
157 // These logic only applies to 'stretch' since the rest of the alignment
158 // values don't change the size of the box.
159 // In any case, the items' overrideSize will be cleared and recomputed (if
160 // necessary) as part of the Grid layout logic, triggered by this style
161 // change.
162 for (LayoutBox* child = FirstInFlowChildBox(); child;
163 child = child->NextInFlowSiblingBox()) {
164 if (SelfAlignmentChangedSize(kGridRowAxis, *old_style, new_style,
165 *child) ||
166 SelfAlignmentChangedSize(kGridColumnAxis, *old_style, new_style,
167 *child)) {
168 child->SetNeedsLayout(layout_invalidation_reason::kGridChanged);
169 }
170 }
171 }
172
173 // FIXME: The following checks could be narrowed down if we kept track of
174 // which type of grid items we have:
175 // - explicit grid size changes impact negative explicitely positioned and
176 // auto-placed grid items.
177 // - named grid lines only impact grid items with named grid lines.
178 // - auto-flow changes only impacts auto-placed children.
179
180 if (ExplicitGridDidResize(*old_style) ||
181 NamedGridLinesDefinitionDidChange(*old_style) ||
182 old_style->GetGridAutoFlow() != StyleRef().GetGridAutoFlow() ||
183 (diff.NeedsLayout() && (StyleRef().GridAutoRepeatColumns().size() ||
184 StyleRef().GridAutoRepeatRows().size())))
185 DirtyGrid();
186 }
187
ExplicitGridDidResize(const ComputedStyle & old_style) const188 bool LayoutGrid::ExplicitGridDidResize(const ComputedStyle& old_style) const {
189 NOT_DESTROYED();
190 return old_style.GridTemplateColumns().LegacyTrackList().size() !=
191 StyleRef().GridTemplateColumns().LegacyTrackList().size() ||
192 old_style.GridTemplateRows().LegacyTrackList().size() !=
193 StyleRef().GridTemplateRows().LegacyTrackList().size() ||
194 old_style.NamedGridAreaColumnCount() !=
195 StyleRef().NamedGridAreaColumnCount() ||
196 old_style.NamedGridAreaRowCount() !=
197 StyleRef().NamedGridAreaRowCount() ||
198 old_style.GridAutoRepeatColumns().size() !=
199 StyleRef().GridAutoRepeatColumns().size() ||
200 old_style.GridAutoRepeatRows().size() !=
201 StyleRef().GridAutoRepeatRows().size();
202 }
203
NamedGridLinesDefinitionDidChange(const ComputedStyle & old_style) const204 bool LayoutGrid::NamedGridLinesDefinitionDidChange(
205 const ComputedStyle& old_style) const {
206 NOT_DESTROYED();
207 return old_style.NamedGridRowLines() != StyleRef().NamedGridRowLines() ||
208 old_style.NamedGridColumnLines() !=
209 StyleRef().NamedGridColumnLines() ||
210 old_style.ImplicitNamedGridRowLines() !=
211 StyleRef().ImplicitNamedGridRowLines() ||
212 old_style.ImplicitNamedGridColumnLines() !=
213 StyleRef().ImplicitNamedGridColumnLines();
214 }
215
ComputeTrackSizesForDefiniteSize(GridTrackSizingDirection direction,LayoutUnit available_space)216 void LayoutGrid::ComputeTrackSizesForDefiniteSize(
217 GridTrackSizingDirection direction,
218 LayoutUnit available_space) {
219 NOT_DESTROYED();
220 track_sizing_algorithm_.Setup(direction, NumTracks(direction, *grid_),
221 available_space);
222 track_sizing_algorithm_.Run();
223
224 #if DCHECK_IS_ON()
225 DCHECK(track_sizing_algorithm_.TracksAreWiderThanMinTrackBreadth());
226 #endif
227 }
228
RepeatTracksSizingIfNeeded(LayoutUnit available_space_for_columns,LayoutUnit available_space_for_rows)229 void LayoutGrid::RepeatTracksSizingIfNeeded(
230 LayoutUnit available_space_for_columns,
231 LayoutUnit available_space_for_rows) {
232 NOT_DESTROYED();
233 // In orthogonal flow cases column track's size is determined by using the
234 // computed row track's size, which it was estimated during the first cycle of
235 // the sizing algorithm.
236 // TODO (lajava): these are just some of the cases which may require
237 // a new cycle of the sizing algorithm; there may be more. In addition, not
238 // all the cases with orthogonal flows require this extra cycle; we need a
239 // more specific condition to detect whether child's min-content contribution
240 // has changed or not.
241 if (!has_any_orthogonal_item_ &&
242 !track_sizing_algorithm_.HasAnyPercentSizedRowsIndefiniteHeight())
243 return;
244
245 // TODO (lajava): Whenever the min-content contribution of a grid item changes
246 // we may need to update the grid container's intrinsic width. The new
247 // intrinsic width may also affect the extra Track Sizing algorithm cycles we
248 // are about to execute.
249 // https://crbug.com/704713
250 // https://github.com/w3c/csswg-drafts/issues/1039
251
252 // Hence we need to repeat computeUsedBreadthOfGridTracks for both, columns
253 // and rows, to determine the final values.
254 ComputeTrackSizesForDefiniteSize(kForColumns, available_space_for_columns);
255 ComputeContentPositionAndDistributionOffset(
256 kForColumns, track_sizing_algorithm_.FreeSpace(kForColumns).value(),
257 NonCollapsedTracks(kForColumns));
258 ComputeTrackSizesForDefiniteSize(kForRows, available_space_for_rows);
259 ComputeContentPositionAndDistributionOffset(
260 kForRows, track_sizing_algorithm_.FreeSpace(kForRows).value(),
261 NonCollapsedTracks(kForRows));
262 }
263
UpdateBlockLayout(bool relayout_children)264 void LayoutGrid::UpdateBlockLayout(bool relayout_children) {
265 NOT_DESTROYED();
266 DCHECK(NeedsLayout());
267
268 // We cannot perform a |SimplifiedLayout()| with a dirty grid.
269 if (!relayout_children && !grid_->NeedsItemsPlacement() && SimplifiedLayout())
270 return;
271
272 SubtreeLayoutScope layout_scope(*this);
273
274 PaintLayerScrollableArea::DelayScrollOffsetClampScope delay_clamp_scope;
275
276 {
277 // LayoutState needs this deliberate scope to pop before updating scroll
278 // information (which may trigger relayout).
279 LayoutState state(*this);
280
281 LayoutSize previous_size = Size();
282 has_definite_logical_height_ = HasDefiniteLogicalHeight();
283
284 has_any_orthogonal_item_ = false;
285 for (auto* child = FirstInFlowChildBox(); child;
286 child = child->NextInFlowSiblingBox()) {
287 // Grid's layout logic controls the grid item's override height, hence
288 // we need to clear any override height set previously, so it doesn't
289 // interfere in current layout execution.
290 // Grid never uses the override width, that's why we don't need to clear
291 // it.
292 child->ClearOverrideLogicalHeight();
293
294 // We may need to repeat the track sizing in case of any grid item was
295 // orthogonal.
296 if (GridLayoutUtils::IsOrthogonalChild(*this, *child))
297 has_any_orthogonal_item_ = true;
298
299 // We keep a cache of items with baseline as alignment values so
300 // that we only compute the baseline shims for such items. This
301 // cache is needed for performance related reasons due to the
302 // cost of evaluating the item's participation in a baseline
303 // context during the track sizing algorithm.
304 if (IsBaselineAlignmentForChild(*child, kGridColumnAxis)) {
305 track_sizing_algorithm_.CacheBaselineAlignedItem(*child,
306 kGridColumnAxis);
307 }
308 if (IsBaselineAlignmentForChild(*child, kGridRowAxis)) {
309 track_sizing_algorithm_.CacheBaselineAlignedItem(*child, kGridRowAxis);
310 }
311 }
312 baseline_items_cached_ = true;
313 UpdateLogicalWidth();
314
315 TextAutosizer::LayoutScope text_autosizer_layout_scope(this, &layout_scope);
316
317 LayoutUnit available_space_for_columns = AvailableLogicalWidth();
318 PlaceItemsOnGrid(track_sizing_algorithm_, available_space_for_columns);
319
320 track_sizing_algorithm_.SetAvailableSpace(kForColumns,
321 available_space_for_columns);
322 PerformGridItemsPreLayout(track_sizing_algorithm_);
323
324 // 1- First, the track sizing algorithm is used to resolve the sizes of the
325 // grid columns.
326 // At this point the logical width is always definite as the above call to
327 // updateLogicalWidth() properly resolves intrinsic sizes. We cannot do the
328 // same for heights though because many code paths inside
329 // updateLogicalHeight() require a previous call to setLogicalHeight() to
330 // resolve heights properly (like for positioned items for example).
331 ComputeTrackSizesForDefiniteSize(kForColumns, available_space_for_columns);
332
333 // 1.5- Compute Content Distribution offsets for column tracks
334 ComputeContentPositionAndDistributionOffset(
335 kForColumns, track_sizing_algorithm_.FreeSpace(kForColumns).value(),
336 NonCollapsedTracks(kForColumns));
337
338 // 2- Next, the track sizing algorithm resolves the sizes of the grid rows,
339 // using the grid column sizes calculated in the previous step.
340 bool recompute_with_track_based_height = false;
341 if (CachedHasDefiniteLogicalHeight()) {
342 ComputeTrackSizesForDefiniteSize(
343 kForRows, AvailableLogicalHeight(kExcludeMarginBorderPadding));
344 } else if (HasOverrideIntrinsicContentLogicalHeight()) {
345 ComputeTrackSizesForDefiniteSize(kForRows,
346 OverrideIntrinsicContentLogicalHeight());
347 } else {
348 ComputeTrackSizesForIndefiniteSize(track_sizing_algorithm_, kForRows);
349 if (ShouldApplySizeContainment())
350 recompute_with_track_based_height = true;
351 }
352 LayoutUnit track_based_logical_height =
353 track_sizing_algorithm_.ComputeTrackBasedSize() +
354 BorderAndPaddingLogicalHeight();
355 if (recompute_with_track_based_height)
356 ComputeTrackSizesForDefiniteSize(kForRows, track_based_logical_height);
357
358 // TODO(rego): We shouldn't need this once crbug.com/906530 is fixed.
359 // Right now we need this because
360 // LayoutBox::ComputeContentAndScrollbarLogicalHeightUsing() is adding the
361 // ScrollbarLogicalHeight() for the intrinsic height cases. But that's
362 // causing more problems as described in the bug linked before.
363 if (!StyleRef().LogicalHeight().IsContentOrIntrinsic())
364 track_based_logical_height += ComputeLogicalScrollbars().BlockSum();
365
366 SetLogicalHeight(track_based_logical_height);
367 UpdateLogicalHeight();
368
369 // Once grid's indefinite height is resolved, we can compute the
370 // available free space for Content Alignment.
371 if (!CachedHasDefiniteLogicalHeight()) {
372 track_sizing_algorithm_.SetFreeSpace(
373 kForRows, LogicalHeight() - track_based_logical_height);
374 }
375
376 // 2.5- Compute Content Distribution offsets for rows tracks
377 ComputeContentPositionAndDistributionOffset(
378 kForRows, track_sizing_algorithm_.FreeSpace(kForRows).value(),
379 NonCollapsedTracks(kForRows));
380
381 // 3- If the min-content contribution of any grid items have changed based
382 // on the row sizes calculated in step 2, steps 1 and 2 are repeated with
383 // the new min-content contribution (once only).
384 RepeatTracksSizingIfNeeded(available_space_for_columns,
385 ContentLogicalHeight());
386
387 // Grid container should have the minimum height of a line if it's editable.
388 // That doesn't affect track sizing though.
389 if (HasLineIfEmpty())
390 SetLogicalHeight(
391 std::max(LogicalHeight(), MinimumLogicalHeightForEmptyLine()));
392
393 LayoutGridItems();
394 track_sizing_algorithm_.Reset();
395
396 if (NumTracks(kForRows, *grid_) > 1u && StyleRef().RowGap() &&
397 StyleRef().RowGap()->IsPercentOrCalc()) {
398 UseCounter::Count(GetDocument(), WebFeature::kGridRowGapPercent);
399 if (!CachedHasDefiniteLogicalHeight()) {
400 UseCounter::Count(GetDocument(),
401 WebFeature::kGridRowGapPercentIndefinite);
402 }
403 }
404
405 if (Size() != previous_size)
406 relayout_children = true;
407
408 LayoutPositionedObjects(relayout_children || IsDocumentElement());
409
410 ComputeLayoutOverflow(ClientLogicalBottom());
411 }
412
413 UpdateAfterLayout();
414
415 ClearNeedsLayout();
416
417 track_sizing_algorithm_.ClearBaselineItemsCache();
418 baseline_items_cached_ = false;
419 }
420
GridGap(GridTrackSizingDirection direction,base::Optional<LayoutUnit> available_size) const421 LayoutUnit LayoutGrid::GridGap(
422 GridTrackSizingDirection direction,
423 base::Optional<LayoutUnit> available_size) const {
424 NOT_DESTROYED();
425 const base::Optional<Length>& gap =
426 direction == kForColumns ? StyleRef().ColumnGap() : StyleRef().RowGap();
427 if (!gap)
428 return LayoutUnit();
429
430 return ValueForLength(*gap, available_size.value_or(LayoutUnit()));
431 }
432
GridGap(GridTrackSizingDirection direction) const433 LayoutUnit LayoutGrid::GridGap(GridTrackSizingDirection direction) const {
434 NOT_DESTROYED();
435 LayoutUnit available_size;
436 bool is_row_axis = direction == kForColumns;
437
438 const base::Optional<Length>& gap =
439 is_row_axis ? StyleRef().ColumnGap() : StyleRef().RowGap();
440 if (!gap)
441 return LayoutUnit();
442
443 if (gap->IsPercentOrCalc()) {
444 available_size =
445 is_row_axis ? AvailableLogicalWidth() : ContentLogicalHeight();
446 }
447
448 // TODO(rego): Maybe we could cache the computed percentage as a performance
449 // improvement.
450 return ValueForLength(*gap, available_size);
451 }
452
GuttersSize(const Grid & grid,GridTrackSizingDirection direction,size_t start_line,size_t span,base::Optional<LayoutUnit> available_size) const453 LayoutUnit LayoutGrid::GuttersSize(
454 const Grid& grid,
455 GridTrackSizingDirection direction,
456 size_t start_line,
457 size_t span,
458 base::Optional<LayoutUnit> available_size) const {
459 NOT_DESTROYED();
460 if (span <= 1)
461 return LayoutUnit();
462
463 LayoutUnit gap = GridGap(direction, available_size);
464
465 // Fast path, no collapsing tracks.
466 if (!grid.HasAutoRepeatEmptyTracks(direction))
467 return gap * (span - 1);
468
469 // If there are collapsing tracks we need to be sure that gutters are properly
470 // collapsed. Apart from that, if we have a collapsed track in the edges of
471 // the span we're considering, we need to move forward (or backwards) in order
472 // to know whether the collapsed tracks reach the end of the grid (so the gap
473 // becomes 0) or there is a non empty track before that.
474
475 LayoutUnit gap_accumulator;
476 size_t end_line = start_line + span;
477
478 for (size_t line = start_line; line < end_line - 1; ++line) {
479 if (!grid.IsEmptyAutoRepeatTrack(direction, line))
480 gap_accumulator += gap;
481 }
482
483 // The above loop adds one extra gap for trailing collapsed tracks.
484 if (gap_accumulator && grid.IsEmptyAutoRepeatTrack(direction, end_line - 1)) {
485 DCHECK_GE(gap_accumulator, gap);
486 gap_accumulator -= gap;
487 }
488
489 // If the startLine is the start line of a collapsed track we need to go
490 // backwards till we reach a non collapsed track. If we find a non collapsed
491 // track we need to add that gap.
492 size_t non_empty_tracks_before_start_line = 0;
493 if (start_line && grid.IsEmptyAutoRepeatTrack(direction, start_line)) {
494 non_empty_tracks_before_start_line = start_line;
495 auto begin = grid.AutoRepeatEmptyTracks(direction)->begin();
496 for (auto it = begin; *it != start_line; ++it) {
497 DCHECK(non_empty_tracks_before_start_line);
498 --non_empty_tracks_before_start_line;
499 }
500 if (non_empty_tracks_before_start_line)
501 gap_accumulator += gap;
502 }
503
504 // If the endLine is the end line of a collapsed track we need to go forward
505 // till we reach a non collapsed track. If we find a non collapsed track we
506 // need to add that gap.
507 if (grid.IsEmptyAutoRepeatTrack(direction, end_line - 1)) {
508 size_t non_empty_tracks_after_end_line =
509 grid.NumTracks(direction) - end_line;
510 auto current_empty_track =
511 grid.AutoRepeatEmptyTracks(direction)->find(end_line - 1);
512 auto end_empty_track = grid.AutoRepeatEmptyTracks(direction)->end();
513 // HashSet iterators do not implement operator- so we have to manually
514 // iterate to know the number of remaining empty tracks.
515 for (auto it = ++current_empty_track; it != end_empty_track; ++it) {
516 DCHECK(non_empty_tracks_after_end_line);
517 --non_empty_tracks_after_end_line;
518 }
519 if (non_empty_tracks_after_end_line) {
520 // We shouldn't count the gap twice if the span starts and ends
521 // in a collapsed track bewtween two non-empty tracks.
522 if (!non_empty_tracks_before_start_line)
523 gap_accumulator += gap;
524 } else if (non_empty_tracks_before_start_line) {
525 // We shouldn't count the gap if the the span starts and ends in
526 // a collapsed but there isn't non-empty tracks afterwards (it's
527 // at the end of the grid).
528 gap_accumulator -= gap;
529 }
530 }
531
532 return gap_accumulator;
533 }
534
ComputeIntrinsicLogicalWidths() const535 MinMaxSizes LayoutGrid::ComputeIntrinsicLogicalWidths() const {
536 NOT_DESTROYED();
537 MinMaxSizes sizes;
538 sizes +=
539 BorderAndPaddingLogicalWidth() + ComputeLogicalScrollbars().InlineSum();
540
541 if (HasOverrideIntrinsicContentLogicalWidth()) {
542 sizes += OverrideIntrinsicContentLogicalWidth();
543 return sizes;
544 }
545
546 std::unique_ptr<Grid> grid = Grid::Create(this);
547 GridTrackSizingAlgorithm algorithm(this, *grid);
548 PlaceItemsOnGrid(algorithm, base::nullopt);
549
550 PerformGridItemsPreLayout(algorithm);
551
552 if (baseline_items_cached_) {
553 algorithm.CopyBaselineItemsCache(track_sizing_algorithm_, kGridRowAxis);
554 } else {
555 for (auto* child = FirstInFlowChildBox(); child;
556 child = child->NextInFlowSiblingBox()) {
557 if (IsBaselineAlignmentForChild(*child, kGridRowAxis)) {
558 algorithm.CacheBaselineAlignedItem(*child, kGridRowAxis);
559 }
560 }
561 }
562
563 ComputeTrackSizesForIndefiniteSize(algorithm, kForColumns);
564
565 size_t number_of_tracks = algorithm.Tracks(kForColumns).size();
566 LayoutUnit total_gutters_size = GuttersSize(
567 algorithm.GetGrid(), kForColumns, 0, number_of_tracks, base::nullopt);
568
569 sizes.min_size += algorithm.MinContentSize() + total_gutters_size;
570 sizes.max_size += algorithm.MaxContentSize() + total_gutters_size;
571 return sizes;
572 }
573
ComputeTrackSizesForIndefiniteSize(GridTrackSizingAlgorithm & algo,GridTrackSizingDirection direction) const574 void LayoutGrid::ComputeTrackSizesForIndefiniteSize(
575 GridTrackSizingAlgorithm& algo,
576 GridTrackSizingDirection direction) const {
577 NOT_DESTROYED();
578 const Grid& grid = algo.GetGrid();
579 algo.Setup(direction, NumTracks(direction, grid), base::nullopt);
580 algo.Run();
581
582 #if DCHECK_IS_ON()
583 DCHECK(algo.TracksAreWiderThanMinTrackBreadth());
584 #endif
585 }
586
OverrideIntrinsicContentLogicalSize(GridTrackSizingDirection direction) const587 base::Optional<LayoutUnit> LayoutGrid::OverrideIntrinsicContentLogicalSize(
588 GridTrackSizingDirection direction) const {
589 NOT_DESTROYED();
590 if (direction == kForColumns && HasOverrideIntrinsicContentLogicalWidth())
591 return OverrideIntrinsicContentLogicalWidth();
592 if (direction == kForRows && HasOverrideIntrinsicContentLogicalHeight())
593 return OverrideIntrinsicContentLogicalHeight();
594 return base::nullopt;
595 }
596
OverrideContainingBlockContentSizeForChild(const LayoutBox & child,GridTrackSizingDirection direction)597 LayoutUnit LayoutGrid::OverrideContainingBlockContentSizeForChild(
598 const LayoutBox& child,
599 GridTrackSizingDirection direction) {
600 return direction == kForColumns
601 ? child.OverrideContainingBlockContentLogicalWidth()
602 : child.OverrideContainingBlockContentLogicalHeight();
603 }
604
605 // Unfortunately there are still many layout methods that return -1 for
606 // non-resolvable sizes. We prefer to represent them with base::nullopt.
ConvertLayoutUnitToOptional(LayoutUnit size)607 static base::Optional<LayoutUnit> ConvertLayoutUnitToOptional(LayoutUnit size) {
608 if (size == -1)
609 return base::nullopt;
610 return size;
611 }
612
ComputeAutoRepeatTracksCount(GridTrackSizingDirection direction,base::Optional<LayoutUnit> available_size) const613 size_t LayoutGrid::ComputeAutoRepeatTracksCount(
614 GridTrackSizingDirection direction,
615 base::Optional<LayoutUnit> available_size) const {
616 NOT_DESTROYED();
617 DCHECK(!available_size || available_size.value() != -1);
618 bool is_row_axis = direction == kForColumns;
619 // Since auto-fit collapses empty tracks, and contain: size dictates that
620 // children should be ignored for the purposes of layout, we can conclude that
621 // if these conditions hold we have 0 repetitions.
622 if (ShouldApplySizeContainment() &&
623 ((is_row_axis &&
624 StyleRef().GridAutoRepeatColumnsType() == AutoRepeatType::kAutoFit) ||
625 (!is_row_axis &&
626 StyleRef().GridAutoRepeatRowsType() == AutoRepeatType::kAutoFit)))
627 return 0;
628 const auto& auto_repeat_tracks = is_row_axis
629 ? StyleRef().GridAutoRepeatColumns()
630 : StyleRef().GridAutoRepeatRows();
631 size_t auto_repeat_track_list_length = auto_repeat_tracks.size();
632
633 if (!auto_repeat_track_list_length)
634 return 0;
635
636 bool needs_to_fulfill_minimum_size = false;
637 if (!available_size) {
638 const Length& max_size = is_row_axis ? StyleRef().LogicalMaxWidth()
639 : StyleRef().LogicalMaxHeight();
640 base::Optional<LayoutUnit> containing_block_available_size;
641 LayoutUnit available_max_size = LayoutUnit();
642 if (max_size.IsSpecified()) {
643 if (max_size.IsPercentOrCalc()) {
644 containing_block_available_size =
645 is_row_axis ? ContainingBlockLogicalWidthForContent()
646 : ContainingBlockLogicalHeightForContent(
647 kExcludeMarginBorderPadding);
648 }
649 LayoutUnit max_size_value = ValueForLength(
650 max_size, containing_block_available_size.value_or(LayoutUnit()));
651 available_max_size =
652 is_row_axis
653 ? AdjustContentBoxLogicalWidthForBoxSizing(max_size_value)
654 : AdjustContentBoxLogicalHeightForBoxSizing(max_size_value);
655 }
656
657 base::Optional<LayoutUnit> intrinsic_size_override =
658 OverrideIntrinsicContentLogicalSize(direction);
659
660 const Length& min_size = is_row_axis ? StyleRef().LogicalMinWidth()
661 : StyleRef().LogicalMinHeight();
662 if (!available_max_size && !min_size.IsSpecified() &&
663 !intrinsic_size_override) {
664 return auto_repeat_track_list_length;
665 }
666
667 LayoutUnit available_min_size = LayoutUnit();
668 if (min_size.IsSpecified()) {
669 if (!containing_block_available_size && min_size.IsPercentOrCalc()) {
670 containing_block_available_size =
671 is_row_axis ? ContainingBlockLogicalWidthForContent()
672 : ContainingBlockLogicalHeightForContent(
673 kExcludeMarginBorderPadding);
674 }
675 LayoutUnit min_size_value = ValueForLength(
676 min_size, containing_block_available_size.value_or(LayoutUnit()));
677 available_min_size =
678 is_row_axis
679 ? AdjustContentBoxLogicalWidthForBoxSizing(min_size_value)
680 : AdjustContentBoxLogicalHeightForBoxSizing(min_size_value);
681 }
682
683 // See https://drafts.csswg.org/css-grid/#auto-repeat for explanation of why
684 // we use needs_to_fulfill_minimum_size. Note that we can treat the
685 // intrinsic-size similar to min-size when filling the remainder of space.
686 // That is, we should fill the intrinsic size fully.
687 if (!max_size.IsSpecified() &&
688 (min_size.IsSpecified() || intrinsic_size_override)) {
689 needs_to_fulfill_minimum_size = true;
690 }
691
692 // Now we need to determine the available size.
693 // We start with the maximum of all of the values. Then, we need to see if
694 // max-size is breached. If it is, then we can shrink the size back up to
695 // the max of min-size and max-size. This is because we can ignore
696 // intrinsic-size in this situation since the min- and max- sizes take
697 // priority.
698 auto available_intrinsic_size =
699 intrinsic_size_override.value_or(LayoutUnit());
700 available_size =
701 std::max(std::max(available_min_size, available_intrinsic_size),
702 available_max_size);
703 if (max_size.IsSpecified() && available_max_size < available_size) {
704 available_size = std::max(available_min_size, available_max_size);
705 }
706 }
707
708 LayoutUnit auto_repeat_tracks_size;
709 for (auto auto_track_size : auto_repeat_tracks) {
710 DCHECK(auto_track_size.MinTrackBreadth().IsLength());
711 DCHECK(!auto_track_size.MinTrackBreadth().IsFlex());
712 bool has_definite_max_track_sizing_function =
713 auto_track_size.MaxTrackBreadth().IsLength() &&
714 !auto_track_size.MaxTrackBreadth().IsContentSized();
715 const Length& track_length =
716 has_definite_max_track_sizing_function
717 ? auto_track_size.MaxTrackBreadth().length()
718 : auto_track_size.MinTrackBreadth().length();
719 auto_repeat_tracks_size +=
720 ValueForLength(track_length, available_size.value());
721 }
722 // For the purpose of finding the number of auto-repeated tracks, the UA must
723 // floor the track size to a UA-specified value to avoid division by zero. It
724 // is suggested that this floor be 1px.
725 auto_repeat_tracks_size =
726 std::max<LayoutUnit>(LayoutUnit(1), auto_repeat_tracks_size);
727
728 // There will be always at least 1 auto-repeat track, so take it already into
729 // account when computing the total track size.
730 LayoutUnit tracks_size = auto_repeat_tracks_size;
731 const Vector<GridTrackSize>& track_sizes =
732 is_row_axis ? StyleRef().GridTemplateColumns().LegacyTrackList()
733 : StyleRef().GridTemplateRows().LegacyTrackList();
734
735 for (const auto& track : track_sizes) {
736 bool has_definite_max_track_breadth =
737 track.MaxTrackBreadth().IsLength() &&
738 !track.MaxTrackBreadth().IsContentSized();
739 DCHECK(has_definite_max_track_breadth ||
740 (track.MinTrackBreadth().IsLength() &&
741 !track.MinTrackBreadth().IsContentSized()));
742 tracks_size += ValueForLength(has_definite_max_track_breadth
743 ? track.MaxTrackBreadth().length()
744 : track.MinTrackBreadth().length(),
745 available_size.value());
746 }
747
748 // Add gutters as if there where only 1 auto repeat track. Gaps between auto
749 // repeat tracks will be added later when computing the repetitions.
750 LayoutUnit gap_size = GridGap(direction, available_size);
751 tracks_size +=
752 gap_size * (track_sizes.size() + auto_repeat_track_list_length - 1);
753
754 LayoutUnit free_space = available_size.value() - tracks_size;
755 if (free_space <= 0)
756 return auto_repeat_track_list_length;
757
758 LayoutUnit auto_repeat_size_with_gap =
759 auto_repeat_tracks_size + gap_size * auto_repeat_track_list_length;
760
761 size_t repetitions = 1 + (free_space / auto_repeat_size_with_gap).ToInt();
762 free_space -= auto_repeat_size_with_gap * (repetitions - 1);
763
764 // Provided the grid container does not have a definite size or max-size in
765 // the relevant axis, if the min size is definite then the number of
766 // repetitions is the smallest positive integer that fulfills that
767 // minimum requirement. If after determining the repetitions, we still have
768 // free space, then we need one more repetition to ensure we fill at least all
769 // of the space.
770 if (needs_to_fulfill_minimum_size && free_space)
771 ++repetitions;
772
773 return repetitions * auto_repeat_track_list_length;
774 }
775
776 std::unique_ptr<OrderedTrackIndexSet>
ComputeEmptyTracksForAutoRepeat(Grid & grid,GridTrackSizingDirection direction) const777 LayoutGrid::ComputeEmptyTracksForAutoRepeat(
778 Grid& grid,
779 GridTrackSizingDirection direction) const {
780 NOT_DESTROYED();
781 bool is_row_axis = direction == kForColumns;
782 if ((is_row_axis &&
783 StyleRef().GridAutoRepeatColumnsType() != AutoRepeatType::kAutoFit) ||
784 (!is_row_axis &&
785 StyleRef().GridAutoRepeatRowsType() != AutoRepeatType::kAutoFit))
786 return nullptr;
787
788 std::unique_ptr<OrderedTrackIndexSet> empty_track_indexes;
789 size_t insertion_point =
790 is_row_axis ? StyleRef().GridAutoRepeatColumnsInsertionPoint()
791 : StyleRef().GridAutoRepeatRowsInsertionPoint();
792 size_t first_auto_repeat_track =
793 insertion_point + grid.ExplicitGridStart(direction);
794 size_t last_auto_repeat_track =
795 first_auto_repeat_track + grid.AutoRepeatTracks(direction);
796
797 if (!grid.HasGridItems()) {
798 empty_track_indexes = std::make_unique<OrderedTrackIndexSet>();
799 for (size_t track_index = first_auto_repeat_track;
800 track_index < last_auto_repeat_track; ++track_index)
801 empty_track_indexes->insert(track_index);
802 } else {
803 for (size_t track_index = first_auto_repeat_track;
804 track_index < last_auto_repeat_track; ++track_index) {
805 auto iterator = grid.CreateIterator(direction, track_index);
806 if (!iterator->NextGridItem()) {
807 if (!empty_track_indexes)
808 empty_track_indexes = std::make_unique<OrderedTrackIndexSet>();
809 empty_track_indexes->insert(track_index);
810 }
811 }
812 }
813 return empty_track_indexes;
814 }
815
ClampAutoRepeatTracks(GridTrackSizingDirection direction,size_t auto_repeat_tracks) const816 size_t LayoutGrid::ClampAutoRepeatTracks(GridTrackSizingDirection direction,
817 size_t auto_repeat_tracks) const {
818 NOT_DESTROYED();
819 if (!auto_repeat_tracks)
820 return 0;
821
822 size_t insertion_point =
823 direction == kForColumns
824 ? StyleRef().GridAutoRepeatColumnsInsertionPoint()
825 : StyleRef().GridAutoRepeatRowsInsertionPoint();
826
827 if (insertion_point == 0)
828 return std::min<size_t>(auto_repeat_tracks, kGridMaxTracks);
829
830 if (insertion_point >= kGridMaxTracks)
831 return 0;
832
833 return std::min(auto_repeat_tracks,
834 static_cast<size_t>(kGridMaxTracks) - insertion_point);
835 }
836
837 // TODO(svillar): we shouldn't have to pass the available logical width as
838 // argument. The problem is that availableLogicalWidth() does always return a
839 // value even if we cannot resolve it like when computing the intrinsic size
840 // (preferred widths). That's why we pass the responsibility to the caller who
841 // does know whether the available logical width is indefinite or not.
PlaceItemsOnGrid(GridTrackSizingAlgorithm & algorithm,base::Optional<LayoutUnit> available_logical_width) const842 void LayoutGrid::PlaceItemsOnGrid(
843 GridTrackSizingAlgorithm& algorithm,
844 base::Optional<LayoutUnit> available_logical_width) const {
845 NOT_DESTROYED();
846 Grid& grid = algorithm.GetMutableGrid();
847 size_t auto_repeat_rows = ComputeAutoRepeatTracksCount(
848 kForRows, ConvertLayoutUnitToOptional(
849 AvailableLogicalHeightForPercentageComputation()));
850 size_t auto_repeat_columns =
851 ComputeAutoRepeatTracksCount(kForColumns, available_logical_width);
852
853 auto_repeat_rows = ClampAutoRepeatTracks(kForRows, auto_repeat_rows);
854 auto_repeat_columns = ClampAutoRepeatTracks(kForColumns, auto_repeat_columns);
855
856 if (auto_repeat_rows != grid.AutoRepeatTracks(kForRows) ||
857 auto_repeat_columns != grid.AutoRepeatTracks(kForColumns)) {
858 grid.SetNeedsItemsPlacement(true);
859 grid.SetAutoRepeatTracks(auto_repeat_rows, auto_repeat_columns);
860 }
861
862 if (!grid.NeedsItemsPlacement())
863 return;
864
865 DCHECK(!grid.HasGridItems());
866 PopulateExplicitGridAndOrderIterator(grid);
867
868 Vector<LayoutBox*> auto_major_axis_auto_grid_items;
869 Vector<LayoutBox*> specified_major_axis_auto_grid_items;
870 #if DCHECK_IS_ON()
871 DCHECK(!grid.HasAnyGridItemPaintOrder());
872 #endif
873 size_t child_index = 0;
874 for (LayoutBox* child = grid.GetOrderIterator().First(); child;
875 child = grid.GetOrderIterator().Next()) {
876 if (child->IsOutOfFlowPositioned())
877 continue;
878
879 // Grid items should use the grid area sizes instead of the containing block
880 // (grid container) sizes, we initialize the overrides here if needed to
881 // ensure it.
882 if (!child->HasOverrideContainingBlockContentLogicalWidth())
883 child->SetOverrideContainingBlockContentLogicalWidth(LayoutUnit());
884 if (!child->HasOverrideContainingBlockContentLogicalHeight())
885 child->SetOverrideContainingBlockContentLogicalHeight(LayoutUnit(-1));
886
887 grid.SetGridItemPaintOrder(*child, child_index++);
888
889 GridArea area = grid.GridItemArea(*child);
890 if (!area.rows.IsIndefinite())
891 area.rows.Translate(grid.ExplicitGridStart(kForRows));
892 if (!area.columns.IsIndefinite())
893 area.columns.Translate(grid.ExplicitGridStart(kForColumns));
894
895 if (area.rows.IsIndefinite() || area.columns.IsIndefinite()) {
896 grid.SetGridItemArea(*child, area);
897 GridSpan major_axis_positions =
898 (AutoPlacementMajorAxisDirection() == kForColumns) ? area.columns
899 : area.rows;
900 if (major_axis_positions.IsIndefinite())
901 auto_major_axis_auto_grid_items.push_back(child);
902 else
903 specified_major_axis_auto_grid_items.push_back(child);
904 continue;
905 }
906 grid.Insert(*child, area);
907 }
908
909 #if DCHECK_IS_ON()
910 if (grid.HasGridItems()) {
911 DCHECK_GE(grid.NumTracks(kForRows),
912 GridPositionsResolver::ExplicitGridRowCount(
913 StyleRef(), grid.AutoRepeatTracks(kForRows)));
914 DCHECK_GE(grid.NumTracks(kForColumns),
915 GridPositionsResolver::ExplicitGridColumnCount(
916 StyleRef(), grid.AutoRepeatTracks(kForColumns)));
917 }
918 #endif
919
920 PlaceSpecifiedMajorAxisItemsOnGrid(grid,
921 specified_major_axis_auto_grid_items);
922 PlaceAutoMajorAxisItemsOnGrid(grid, auto_major_axis_auto_grid_items);
923
924 // Compute collapsable tracks for auto-fit.
925 grid.SetAutoRepeatEmptyColumns(
926 ComputeEmptyTracksForAutoRepeat(grid, kForColumns));
927 grid.SetAutoRepeatEmptyRows(ComputeEmptyTracksForAutoRepeat(grid, kForRows));
928
929 grid.SetNeedsItemsPlacement(false);
930
931 #if DCHECK_IS_ON()
932 for (LayoutBox* child = grid.GetOrderIterator().First(); child;
933 child = grid.GetOrderIterator().Next()) {
934 if (child->IsOutOfFlowPositioned())
935 continue;
936
937 GridArea area = grid.GridItemArea(*child);
938 DCHECK(area.rows.IsTranslatedDefinite());
939 DCHECK(area.columns.IsTranslatedDefinite());
940 }
941 #endif
942 }
943
944 // TODO(lajava): Consider rafactoring this code with
945 // LocalFrameView::PrepareOrthogonalWritingModeRootForLayout
PrepareOrthogonalWritingModeRootForLayout(LayoutObject & root)946 static bool PrepareOrthogonalWritingModeRootForLayout(LayoutObject& root) {
947 DCHECK(To<LayoutBox>(root).IsOrthogonalWritingModeRoot());
948 if (!root.NeedsLayout() || root.IsOutOfFlowPositioned() ||
949 root.IsColumnSpanAll() || root.IsTablePart())
950 return false;
951
952 return true;
953 }
954
PerformGridItemsPreLayout(const GridTrackSizingAlgorithm & algorithm) const955 void LayoutGrid::PerformGridItemsPreLayout(
956 const GridTrackSizingAlgorithm& algorithm) const {
957 NOT_DESTROYED();
958 DCHECK(!algorithm.GetGrid().NeedsItemsPlacement());
959 if (!GetDocument().View()->IsInPerformLayout())
960 return;
961 for (auto* child = FirstInFlowChildBox(); child;
962 child = child->NextInFlowSiblingBox()) {
963 // Blink does a pre-layout of all the orthogonal boxes in the layout
964 // tree (see how LocalFrameView::PerformLayout calls its
965 // LayoutOrthogonalWritingModeRoots function). However, grid items
966 // don't participate in this process (see the function
967 // PrepareOrthogonalWritingModeRootForLayout) because it's useless
968 // and even wrong if they don't have their corresponding Grid Area.
969 // TODO(jfernandez): Consider rafactoring this code with
970 // LocalFrameView::LayoutOrthogonalWritingModeRoots
971 if (GridLayoutUtils::IsOrthogonalChild(*this, *child)) {
972 if (PrepareOrthogonalWritingModeRootForLayout(*child)) {
973 UpdateGridAreaLogicalSize(
974 *child, algorithm.EstimatedGridAreaBreadthForChild(*child));
975 child->LayoutIfNeeded();
976 continue;
977 }
978 }
979 // We need to layout the item to know whether it must synthesize its
980 // baseline or not, which may imply a cyclic sizing dependency.
981 // TODO (jfernandez): Can we avoid it ?
982 if (IsBaselineAlignmentForChild(*child)) {
983 if (child->HasRelativeLogicalWidth() ||
984 child->HasRelativeLogicalHeight() ||
985 child->StyleRef().LogicalHeight().IsAuto()) {
986 UpdateGridAreaLogicalSize(
987 *child, algorithm.EstimatedGridAreaBreadthForChild(*child));
988 }
989 child->LayoutIfNeeded();
990 }
991 }
992 }
993
PopulateExplicitGridAndOrderIterator(Grid & grid) const994 void LayoutGrid::PopulateExplicitGridAndOrderIterator(Grid& grid) const {
995 NOT_DESTROYED();
996 OrderIteratorPopulator populator(grid.GetOrderIterator());
997 size_t explicit_row_start = 0;
998 size_t explicit_column_start = 0;
999
1000 size_t auto_repeat_rows = grid.AutoRepeatTracks(kForRows);
1001 size_t auto_repeat_columns = grid.AutoRepeatTracks(kForColumns);
1002 size_t maximum_row_index =
1003 GridPositionsResolver::ExplicitGridRowCount(StyleRef(), auto_repeat_rows);
1004 size_t maximum_column_index = GridPositionsResolver::ExplicitGridColumnCount(
1005 StyleRef(), auto_repeat_columns);
1006
1007 for (LayoutBox* child = FirstInFlowChildBox(); child;
1008 child = child->NextInFlowSiblingBox()) {
1009 populator.CollectChild(child);
1010
1011 // This function bypasses the cache (gridItemArea()) as it is used to
1012 // build it.
1013 GridSpan row_positions =
1014 GridPositionsResolver::ResolveGridPositionsFromStyle(
1015 StyleRef(), child->StyleRef(), kForRows, auto_repeat_rows);
1016 GridSpan column_positions =
1017 GridPositionsResolver::ResolveGridPositionsFromStyle(
1018 StyleRef(), child->StyleRef(), kForColumns, auto_repeat_columns);
1019 grid.SetGridItemArea(*child, GridArea(row_positions, column_positions));
1020
1021 // |positions| is 0 if we need to run the auto-placement algorithm.
1022 if (!row_positions.IsIndefinite()) {
1023 explicit_row_start = std::max<int>(
1024 explicit_row_start, -row_positions.UntranslatedStartLine());
1025 maximum_row_index =
1026 std::max<int>(maximum_row_index, row_positions.UntranslatedEndLine());
1027 } else {
1028 // Grow the grid for items with a definite row span, getting the largest
1029 // such span.
1030 size_t span_size = GridPositionsResolver::SpanSizeForAutoPlacedItem(
1031 child->StyleRef(), kForRows);
1032 maximum_row_index = std::max(maximum_row_index, span_size);
1033 }
1034
1035 if (!column_positions.IsIndefinite()) {
1036 explicit_column_start = std::max<int>(
1037 explicit_column_start, -column_positions.UntranslatedStartLine());
1038 maximum_column_index = std::max<int>(
1039 maximum_column_index, column_positions.UntranslatedEndLine());
1040 } else {
1041 // Grow the grid for items with a definite column span, getting the
1042 // largest such span.
1043 size_t span_size = GridPositionsResolver::SpanSizeForAutoPlacedItem(
1044 child->StyleRef(), kForColumns);
1045 maximum_column_index = std::max(maximum_column_index, span_size);
1046 }
1047 }
1048
1049 grid.SetExplicitGridStart(explicit_row_start, explicit_column_start);
1050 grid.EnsureGridSize(maximum_row_index + explicit_row_start,
1051 maximum_column_index + explicit_column_start);
1052 }
1053
1054 std::unique_ptr<GridArea>
CreateEmptyGridAreaAtSpecifiedPositionsOutsideGrid(const Grid & grid,const LayoutBox & grid_item,GridTrackSizingDirection specified_direction,const GridSpan & specified_positions) const1055 LayoutGrid::CreateEmptyGridAreaAtSpecifiedPositionsOutsideGrid(
1056 const Grid& grid,
1057 const LayoutBox& grid_item,
1058 GridTrackSizingDirection specified_direction,
1059 const GridSpan& specified_positions) const {
1060 NOT_DESTROYED();
1061 GridTrackSizingDirection cross_direction =
1062 specified_direction == kForColumns ? kForRows : kForColumns;
1063 const size_t end_of_cross_direction = grid.NumTracks(cross_direction);
1064 size_t cross_direction_span_size =
1065 GridPositionsResolver::SpanSizeForAutoPlacedItem(grid_item.StyleRef(),
1066 cross_direction);
1067 GridSpan cross_direction_positions = GridSpan::TranslatedDefiniteGridSpan(
1068 end_of_cross_direction,
1069 end_of_cross_direction + cross_direction_span_size);
1070 return std::make_unique<GridArea>(
1071 specified_direction == kForColumns ? cross_direction_positions
1072 : specified_positions,
1073 specified_direction == kForColumns ? specified_positions
1074 : cross_direction_positions);
1075 }
1076
PlaceSpecifiedMajorAxisItemsOnGrid(Grid & grid,const Vector<LayoutBox * > & auto_grid_items) const1077 void LayoutGrid::PlaceSpecifiedMajorAxisItemsOnGrid(
1078 Grid& grid,
1079 const Vector<LayoutBox*>& auto_grid_items) const {
1080 NOT_DESTROYED();
1081 bool is_for_columns = AutoPlacementMajorAxisDirection() == kForColumns;
1082 bool is_grid_auto_flow_dense = StyleRef().IsGridAutoFlowAlgorithmDense();
1083
1084 // Mapping between the major axis tracks (rows or columns) and the last
1085 // auto-placed item's position inserted on that track. This is needed to
1086 // implement "sparse" packing for items locked to a given track.
1087 // See https://drafts.csswg.org/css-grid/#auto-placement-algo
1088 HashMap<unsigned, unsigned, DefaultHash<unsigned>::Hash,
1089 WTF::UnsignedWithZeroKeyHashTraits<unsigned>>
1090 minor_axis_cursors;
1091
1092 for (auto* const auto_grid_item : auto_grid_items) {
1093 GridSpan major_axis_positions =
1094 grid.GridItemSpan(*auto_grid_item, AutoPlacementMajorAxisDirection());
1095 DCHECK(major_axis_positions.IsTranslatedDefinite());
1096 DCHECK(
1097 !grid.GridItemSpan(*auto_grid_item, AutoPlacementMinorAxisDirection())
1098 .IsTranslatedDefinite());
1099 size_t minor_axis_span_size =
1100 GridPositionsResolver::SpanSizeForAutoPlacedItem(
1101 auto_grid_item->StyleRef(), AutoPlacementMinorAxisDirection());
1102 unsigned major_axis_initial_position = major_axis_positions.StartLine();
1103
1104 auto iterator = grid.CreateIterator(
1105 AutoPlacementMajorAxisDirection(), major_axis_positions.StartLine(),
1106 is_grid_auto_flow_dense
1107 ? 0
1108 : minor_axis_cursors.at(major_axis_initial_position));
1109 std::unique_ptr<GridArea> empty_grid_area = iterator->NextEmptyGridArea(
1110 major_axis_positions.IntegerSpan(), minor_axis_span_size);
1111 DCHECK(empty_grid_area);
1112
1113 grid.Insert(*auto_grid_item, *empty_grid_area);
1114
1115 if (!is_grid_auto_flow_dense)
1116 minor_axis_cursors.Set(major_axis_initial_position,
1117 is_for_columns
1118 ? empty_grid_area->rows.StartLine()
1119 : empty_grid_area->columns.StartLine());
1120 }
1121 }
1122
PlaceAutoMajorAxisItemsOnGrid(Grid & grid,const Vector<LayoutBox * > & auto_grid_items) const1123 void LayoutGrid::PlaceAutoMajorAxisItemsOnGrid(
1124 Grid& grid,
1125 const Vector<LayoutBox*>& auto_grid_items) const {
1126 NOT_DESTROYED();
1127 std::pair<size_t, size_t> auto_placement_cursor = std::make_pair(0, 0);
1128 bool is_grid_auto_flow_dense = StyleRef().IsGridAutoFlowAlgorithmDense();
1129
1130 for (auto* const auto_grid_item : auto_grid_items) {
1131 PlaceAutoMajorAxisItemOnGrid(grid, *auto_grid_item, auto_placement_cursor);
1132
1133 // If grid-auto-flow is dense, reset auto-placement cursor.
1134 if (is_grid_auto_flow_dense) {
1135 auto_placement_cursor.first = 0;
1136 auto_placement_cursor.second = 0;
1137 }
1138 }
1139 }
1140
PlaceAutoMajorAxisItemOnGrid(Grid & grid,LayoutBox & grid_item,std::pair<size_t,size_t> & auto_placement_cursor) const1141 void LayoutGrid::PlaceAutoMajorAxisItemOnGrid(
1142 Grid& grid,
1143 LayoutBox& grid_item,
1144 std::pair<size_t, size_t>& auto_placement_cursor) const {
1145 NOT_DESTROYED();
1146 GridSpan minor_axis_positions =
1147 grid.GridItemSpan(grid_item, AutoPlacementMinorAxisDirection());
1148 DCHECK(!grid.GridItemSpan(grid_item, AutoPlacementMajorAxisDirection())
1149 .IsTranslatedDefinite());
1150 size_t major_axis_span_size =
1151 GridPositionsResolver::SpanSizeForAutoPlacedItem(
1152 grid_item.StyleRef(), AutoPlacementMajorAxisDirection());
1153
1154 const size_t end_of_major_axis =
1155 grid.NumTracks(AutoPlacementMajorAxisDirection());
1156 size_t major_axis_auto_placement_cursor =
1157 AutoPlacementMajorAxisDirection() == kForColumns
1158 ? auto_placement_cursor.second
1159 : auto_placement_cursor.first;
1160 size_t minor_axis_auto_placement_cursor =
1161 AutoPlacementMajorAxisDirection() == kForColumns
1162 ? auto_placement_cursor.first
1163 : auto_placement_cursor.second;
1164
1165 std::unique_ptr<GridArea> empty_grid_area;
1166 if (minor_axis_positions.IsTranslatedDefinite()) {
1167 // Move to the next track in major axis if initial position in minor axis is
1168 // before auto-placement cursor.
1169 if (minor_axis_positions.StartLine() < minor_axis_auto_placement_cursor)
1170 major_axis_auto_placement_cursor++;
1171
1172 if (major_axis_auto_placement_cursor < end_of_major_axis) {
1173 auto iterator = grid.CreateIterator(AutoPlacementMinorAxisDirection(),
1174 minor_axis_positions.StartLine(),
1175 major_axis_auto_placement_cursor);
1176 empty_grid_area = iterator->NextEmptyGridArea(
1177 minor_axis_positions.IntegerSpan(), major_axis_span_size);
1178 }
1179
1180 if (!empty_grid_area) {
1181 empty_grid_area = CreateEmptyGridAreaAtSpecifiedPositionsOutsideGrid(
1182 grid, grid_item, AutoPlacementMinorAxisDirection(),
1183 minor_axis_positions);
1184 }
1185 } else {
1186 size_t minor_axis_span_size =
1187 GridPositionsResolver::SpanSizeForAutoPlacedItem(
1188 grid_item.StyleRef(), AutoPlacementMinorAxisDirection());
1189
1190 for (size_t major_axis_index = major_axis_auto_placement_cursor;
1191 major_axis_index < end_of_major_axis; ++major_axis_index) {
1192 auto iterator = grid.CreateIterator(AutoPlacementMajorAxisDirection(),
1193 major_axis_index,
1194 minor_axis_auto_placement_cursor);
1195 empty_grid_area = iterator->NextEmptyGridArea(major_axis_span_size,
1196 minor_axis_span_size);
1197 DCHECK(empty_grid_area);
1198
1199 // Check that it fits in the minor axis direction, as we shouldn't grow
1200 // in that direction here (it was already managed in
1201 // populateExplicitGridAndOrderIterator()).
1202 size_t minor_axis_final_position_index =
1203 AutoPlacementMinorAxisDirection() == kForColumns
1204 ? empty_grid_area->columns.EndLine()
1205 : empty_grid_area->rows.EndLine();
1206 const size_t end_of_minor_axis =
1207 grid.NumTracks(AutoPlacementMinorAxisDirection());
1208 if (minor_axis_final_position_index <= end_of_minor_axis)
1209 break;
1210
1211 // Discard empty grid area as it does not fit in the minor axis
1212 // direction. We don't need to create a new empty grid area yet as we
1213 // might find a valid one in the next iteration.
1214 empty_grid_area.reset();
1215
1216 // As we're moving to the next track in the major axis we should reset the
1217 // auto-placement cursor in the minor axis.
1218 minor_axis_auto_placement_cursor = 0;
1219 }
1220
1221 if (!empty_grid_area)
1222 empty_grid_area = CreateEmptyGridAreaAtSpecifiedPositionsOutsideGrid(
1223 grid, grid_item, AutoPlacementMinorAxisDirection(),
1224 GridSpan::TranslatedDefiniteGridSpan(0, minor_axis_span_size));
1225 }
1226
1227 grid.Insert(grid_item, *empty_grid_area);
1228 // Move auto-placement cursor to the new position.
1229 auto_placement_cursor.first = empty_grid_area->rows.StartLine();
1230 auto_placement_cursor.second = empty_grid_area->columns.StartLine();
1231 }
1232
AutoPlacementMajorAxisDirection() const1233 GridTrackSizingDirection LayoutGrid::AutoPlacementMajorAxisDirection() const {
1234 NOT_DESTROYED();
1235 return StyleRef().IsGridAutoFlowDirectionColumn() ? kForColumns : kForRows;
1236 }
1237
AutoPlacementMinorAxisDirection() const1238 GridTrackSizingDirection LayoutGrid::AutoPlacementMinorAxisDirection() const {
1239 NOT_DESTROYED();
1240 return StyleRef().IsGridAutoFlowDirectionColumn() ? kForRows : kForColumns;
1241 }
1242
DirtyGrid()1243 void LayoutGrid::DirtyGrid() {
1244 NOT_DESTROYED();
1245 if (grid_->NeedsItemsPlacement())
1246 return;
1247
1248 grid_->SetNeedsItemsPlacement(true);
1249 }
1250
TrackSizesForComputedStyle(GridTrackSizingDirection direction) const1251 Vector<LayoutUnit> LayoutGrid::TrackSizesForComputedStyle(
1252 GridTrackSizingDirection direction) const {
1253 NOT_DESTROYED();
1254 bool is_row_axis = direction == kForColumns;
1255 auto& positions = is_row_axis ? column_positions_ : row_positions_;
1256 size_t num_positions = positions.size();
1257 LayoutUnit offset_between_tracks =
1258 is_row_axis ? offset_between_columns_.distribution_offset
1259 : offset_between_rows_.distribution_offset;
1260
1261 Vector<LayoutUnit> tracks;
1262 if (num_positions < 2)
1263 return tracks;
1264
1265 DCHECK(!grid_->NeedsItemsPlacement());
1266 bool has_collapsed_tracks = grid_->HasAutoRepeatEmptyTracks(direction);
1267 LayoutUnit gap = !has_collapsed_tracks ? GridGap(direction) : LayoutUnit();
1268 tracks.ReserveCapacity(num_positions - 1);
1269 for (size_t i = 0; i < num_positions - 2; ++i)
1270 tracks.push_back(positions[i + 1] - positions[i] - offset_between_tracks -
1271 gap);
1272 tracks.push_back(positions[num_positions - 1] - positions[num_positions - 2]);
1273
1274 if (!has_collapsed_tracks)
1275 return tracks;
1276
1277 size_t remaining_empty_tracks =
1278 grid_->AutoRepeatEmptyTracks(direction)->size();
1279 size_t last_line = tracks.size();
1280 gap = GridGap(direction);
1281 for (size_t i = 1; i < last_line; ++i) {
1282 if (grid_->IsEmptyAutoRepeatTrack(direction, i - 1)) {
1283 --remaining_empty_tracks;
1284 } else {
1285 // Remove the gap between consecutive non empty tracks. Remove it also
1286 // just once for an arbitrary number of empty tracks between two non empty
1287 // ones.
1288 bool all_remaining_tracks_are_empty =
1289 remaining_empty_tracks == (last_line - i);
1290 if (!all_remaining_tracks_are_empty ||
1291 !grid_->IsEmptyAutoRepeatTrack(direction, i))
1292 tracks[i - 1] -= gap;
1293 }
1294 }
1295
1296 return tracks;
1297 }
1298
ContentAlignmentNormalBehavior()1299 const StyleContentAlignmentData& LayoutGrid::ContentAlignmentNormalBehavior() {
1300 static const StyleContentAlignmentData kNormalBehavior = {
1301 ContentPosition::kNormal, ContentDistributionType::kStretch};
1302 return kNormalBehavior;
1303 }
1304
OverrideSizeChanged(const LayoutBox & child,GridTrackSizingDirection direction,LayoutSize size)1305 static bool OverrideSizeChanged(const LayoutBox& child,
1306 GridTrackSizingDirection direction,
1307 LayoutSize size) {
1308 if (direction == kForColumns) {
1309 return !child.HasOverrideContainingBlockContentLogicalWidth() ||
1310 child.OverrideContainingBlockContentLogicalWidth() != size.Width();
1311 }
1312 return !child.HasOverrideContainingBlockContentLogicalHeight() ||
1313 child.OverrideContainingBlockContentLogicalHeight() != size.Height();
1314 }
1315
HasRelativeBlockAxisSize(const LayoutGrid & grid,const LayoutBox & child)1316 static bool HasRelativeBlockAxisSize(const LayoutGrid& grid,
1317 const LayoutBox& child) {
1318 return GridLayoutUtils::IsOrthogonalChild(grid, child)
1319 ? child.HasRelativeLogicalWidth() ||
1320 child.StyleRef().LogicalWidth().IsAuto()
1321 : child.HasRelativeLogicalHeight();
1322 }
1323
UpdateGridAreaLogicalSize(LayoutBox & child,LayoutSize grid_area_logical_size) const1324 void LayoutGrid::UpdateGridAreaLogicalSize(
1325 LayoutBox& child,
1326 LayoutSize grid_area_logical_size) const {
1327 NOT_DESTROYED();
1328 // Because the grid area cannot be styled, we don't need to adjust
1329 // the grid breadth to account for 'box-sizing'.
1330 bool grid_area_width_changed =
1331 OverrideSizeChanged(child, kForColumns, grid_area_logical_size);
1332 bool grid_area_height_changed =
1333 OverrideSizeChanged(child, kForRows, grid_area_logical_size);
1334 if (grid_area_width_changed ||
1335 (grid_area_height_changed && HasRelativeBlockAxisSize(*this, child))) {
1336 child.SetSelfNeedsLayoutForAvailableSpace(true);
1337 }
1338
1339 child.SetOverrideContainingBlockContentLogicalWidth(
1340 grid_area_logical_size.Width());
1341 child.SetOverrideContainingBlockContentLogicalHeight(
1342 grid_area_logical_size.Height());
1343 }
1344
LayoutGridItems()1345 void LayoutGrid::LayoutGridItems() {
1346 NOT_DESTROYED();
1347 if (ChildLayoutBlockedByDisplayLock())
1348 return;
1349
1350 PopulateGridPositionsForDirection(kForColumns);
1351 PopulateGridPositionsForDirection(kForRows);
1352
1353 for (LayoutBox* child = FirstChildBox(); child;
1354 child = child->NextSiblingBox()) {
1355 if (child->IsOutOfFlowPositioned()) {
1356 PrepareChildForPositionedLayout(*child);
1357 continue;
1358 }
1359
1360 // Setting the definite grid area's sizes. It may imply that the
1361 // item must perform a layout if its area differs from the one
1362 // used during the track sizing algorithm.
1363 UpdateGridAreaLogicalSize(
1364 *child, LayoutSize(GridAreaBreadthForChildIncludingAlignmentOffsets(
1365 *child, kForColumns),
1366 GridAreaBreadthForChildIncludingAlignmentOffsets(
1367 *child, kForRows)));
1368
1369 // Stretching logic might force a child layout, so we need to run it before
1370 // the layoutIfNeeded call to avoid unnecessary relayouts. This might imply
1371 // that child margins, needed to correctly determine the available space
1372 // before stretching, are not set yet.
1373 ApplyStretchAlignmentToChildIfNeeded(*child);
1374
1375 child->LayoutIfNeeded();
1376
1377 // We need pending layouts to be done in order to compute auto-margins
1378 // properly.
1379 UpdateAutoMarginsInColumnAxisIfNeeded(*child);
1380 UpdateAutoMarginsInRowAxisIfNeeded(*child);
1381
1382 const GridArea& area = grid_->GridItemArea(*child);
1383 #if DCHECK_IS_ON()
1384 DCHECK_LT(area.columns.StartLine(),
1385 track_sizing_algorithm_.Tracks(kForColumns).size());
1386 DCHECK_LT(area.rows.StartLine(),
1387 track_sizing_algorithm_.Tracks(kForRows).size());
1388 #endif
1389 SetLogicalPositionForChild(*child);
1390
1391 // Keep track of children overflowing their grid area as we might need to
1392 // paint them even if the grid-area is not visible. Using physical
1393 // dimensions for simplicity, so we can forget about orthogonalty.
1394 LayoutUnit child_grid_area_height =
1395 child->OverrideContainingBlockContentHeight();
1396 LayoutUnit child_grid_area_width =
1397 child->OverrideContainingBlockContentWidth();
1398 LayoutRect grid_area_rect(
1399 GridAreaLogicalPosition(area),
1400 LayoutSize(child_grid_area_width, child_grid_area_height));
1401 LayoutRect child_overflow_rect = child->FrameRect();
1402 child_overflow_rect.SetSize(child->VisualOverflowRect().Size());
1403 }
1404 }
1405
PrepareChildForPositionedLayout(LayoutBox & child)1406 void LayoutGrid::PrepareChildForPositionedLayout(LayoutBox& child) {
1407 NOT_DESTROYED();
1408 DCHECK(child.IsOutOfFlowPositioned());
1409 child.ContainingBlock()->InsertPositionedObject(&child);
1410
1411 PaintLayer* child_layer = child.Layer();
1412 // Static position of a positioned child should use the content-box
1413 // (https://drafts.csswg.org/css-grid/#static-position).
1414 child_layer->SetStaticInlinePosition(BorderAndPaddingStart());
1415 child_layer->SetStaticBlockPosition(BorderAndPaddingBefore());
1416 }
1417
HasStaticPositionForChild(const LayoutBox & child,GridTrackSizingDirection direction) const1418 bool LayoutGrid::HasStaticPositionForChild(
1419 const LayoutBox& child,
1420 GridTrackSizingDirection direction) const {
1421 NOT_DESTROYED();
1422 return direction == kForColumns ? child.StyleRef().HasStaticInlinePosition(
1423 IsHorizontalWritingMode())
1424 : child.StyleRef().HasStaticBlockPosition(
1425 IsHorizontalWritingMode());
1426 }
1427
LayoutPositionedObjects(bool relayout_children,PositionedLayoutBehavior info)1428 void LayoutGrid::LayoutPositionedObjects(bool relayout_children,
1429 PositionedLayoutBehavior info) {
1430 NOT_DESTROYED();
1431 if (ChildLayoutBlockedByDisplayLock())
1432 return;
1433
1434 column_of_positioned_item_.clear();
1435 row_of_positioned_item_.clear();
1436
1437 TrackedLayoutBoxListHashSet* positioned_descendants = PositionedObjects();
1438 if (!positioned_descendants)
1439 return;
1440
1441 for (auto* child : *positioned_descendants) {
1442 LayoutUnit column_breadth =
1443 GridAreaBreadthForOutOfFlowChild(*child, kForColumns);
1444 LayoutUnit row_breadth = GridAreaBreadthForOutOfFlowChild(*child, kForRows);
1445
1446 child->SetOverrideContainingBlockContentLogicalWidth(column_breadth);
1447 child->SetOverrideContainingBlockContentLogicalHeight(row_breadth);
1448
1449 // Mark for layout as we're resetting the position before and we relay in
1450 // generic layout logic for positioned items in order to get the offsets
1451 // properly resolved.
1452 child->SetNeedsLayout(layout_invalidation_reason::kGridChanged,
1453 kMarkOnlyThis);
1454
1455 LayoutPositionedObject(child, relayout_children, info);
1456
1457 SetLogicalOffsetForChild(*child, kForColumns);
1458 SetLogicalOffsetForChild(*child, kForRows);
1459 }
1460 }
1461
GridAreaBreadthForChildIncludingAlignmentOffsets(const LayoutBox & child,GridTrackSizingDirection direction) const1462 LayoutUnit LayoutGrid::GridAreaBreadthForChildIncludingAlignmentOffsets(
1463 const LayoutBox& child,
1464 GridTrackSizingDirection direction) const {
1465 NOT_DESTROYED();
1466 // We need the cached value when available because Content Distribution
1467 // alignment properties may have some influence in the final grid area
1468 // breadth.
1469 const Vector<GridTrack>& tracks = track_sizing_algorithm_.Tracks(direction);
1470 const GridSpan& span =
1471 track_sizing_algorithm_.GetGrid().GridItemSpan(child, direction);
1472 const Vector<LayoutUnit>& line_positions =
1473 (direction == kForColumns) ? column_positions_ : row_positions_;
1474 LayoutUnit initial_track_position = line_positions[span.StartLine()];
1475 LayoutUnit final_track_position = line_positions[span.EndLine() - 1];
1476 // Track Positions vector stores the 'start' grid line of each track, so we
1477 // have to add last track's baseSize.
1478 return final_track_position - initial_track_position +
1479 tracks[span.EndLine() - 1].BaseSize();
1480 }
1481
PopulateGridPositionsForDirection(GridTrackSizingDirection direction)1482 void LayoutGrid::PopulateGridPositionsForDirection(
1483 GridTrackSizingDirection direction) {
1484 NOT_DESTROYED();
1485 // Since we add alignment offsets and track gutters, grid lines are not always
1486 // adjacent. Hence we will have to assume from now on that we just store
1487 // positions of the initial grid lines of each track, except the last one,
1488 // which is the only one considered as a final grid line of a track.
1489
1490 // The grid container's frame elements (border, padding and <content-position>
1491 // offset) are sensible to the inline-axis flow direction. However, column
1492 // lines positions are 'direction' unaware. This simplification allows us to
1493 // use the same indexes to identify the columns independently on the
1494 // inline-axis direction.
1495 bool is_row_axis = direction == kForColumns;
1496 auto& tracks = track_sizing_algorithm_.Tracks(direction);
1497 size_t number_of_tracks = tracks.size();
1498 size_t number_of_lines = number_of_tracks + 1;
1499 size_t last_line = number_of_lines - 1;
1500 bool has_collapsed_tracks = grid_->HasAutoRepeatEmptyTracks(direction);
1501 size_t number_of_collapsed_tracks =
1502 has_collapsed_tracks ? grid_->AutoRepeatEmptyTracks(direction)->size()
1503 : 0;
1504 const auto& offset =
1505 direction == kForColumns ? offset_between_columns_ : offset_between_rows_;
1506 auto& positions = is_row_axis ? column_positions_ : row_positions_;
1507 positions.resize(number_of_lines);
1508
1509 auto border_and_padding =
1510 is_row_axis ? BorderAndPaddingLogicalLeft() : BorderAndPaddingBefore();
1511 if (is_row_axis) {
1512 if (StyleRef().IsHorizontalWritingMode() &&
1513 !StyleRef().IsLeftToRightDirection())
1514 border_and_padding += ComputeLogicalScrollbars().InlineSum();
1515 } else {
1516 if (StyleRef().GetWritingMode() == WritingMode::kVerticalRl)
1517 border_and_padding += ComputeLogicalScrollbars().BlockSum();
1518 }
1519
1520 positions[0] = border_and_padding + offset.position_offset;
1521 if (number_of_lines > 1) {
1522 // If we have collapsed tracks we just ignore gaps here and add them later
1523 // as we might not compute the gap between two consecutive tracks without
1524 // examining the surrounding ones.
1525 LayoutUnit gap = !has_collapsed_tracks ? GridGap(direction) : LayoutUnit();
1526 size_t next_to_last_line = number_of_lines - 2;
1527 for (size_t i = 0; i < next_to_last_line; ++i)
1528 positions[i + 1] = positions[i] + offset.distribution_offset +
1529 tracks[i].BaseSize() + gap;
1530 positions[last_line] =
1531 positions[next_to_last_line] + tracks[next_to_last_line].BaseSize();
1532
1533 // Adjust collapsed gaps. Collapsed tracks cause the surrounding gutters to
1534 // collapse (they coincide exactly) except on the edges of the grid where
1535 // they become 0.
1536 if (has_collapsed_tracks) {
1537 gap = GridGap(direction);
1538 size_t remaining_empty_tracks = number_of_collapsed_tracks;
1539 LayoutUnit offset_accumulator;
1540 LayoutUnit gap_accumulator;
1541 for (size_t i = 1; i < last_line; ++i) {
1542 if (grid_->IsEmptyAutoRepeatTrack(direction, i - 1)) {
1543 --remaining_empty_tracks;
1544 offset_accumulator += offset.distribution_offset;
1545 } else {
1546 // Add gap between consecutive non empty tracks. Add it also just once
1547 // for an arbitrary number of empty tracks between two non empty ones.
1548 bool all_remaining_tracks_are_empty =
1549 remaining_empty_tracks == (last_line - i);
1550 if (!all_remaining_tracks_are_empty ||
1551 !grid_->IsEmptyAutoRepeatTrack(direction, i))
1552 gap_accumulator += gap;
1553 }
1554 positions[i] += gap_accumulator - offset_accumulator;
1555 }
1556 positions[last_line] += gap_accumulator - offset_accumulator;
1557 }
1558 }
1559 }
1560
ComputeOverflowAlignmentOffset(OverflowAlignment overflow,LayoutUnit track_size,LayoutUnit child_size)1561 static LayoutUnit ComputeOverflowAlignmentOffset(OverflowAlignment overflow,
1562 LayoutUnit track_size,
1563 LayoutUnit child_size) {
1564 LayoutUnit offset = track_size - child_size;
1565 switch (overflow) {
1566 case OverflowAlignment::kSafe:
1567 // If overflow is 'safe', we have to make sure we don't overflow the
1568 // 'start' edge (potentially cause some data loss as the overflow is
1569 // unreachable).
1570 return offset.ClampNegativeToZero();
1571 case OverflowAlignment::kUnsafe:
1572 case OverflowAlignment::kDefault:
1573 // If we overflow our alignment container and overflow is 'true'
1574 // (default), we ignore the overflow and just return the value regardless
1575 // (which may cause data loss as we overflow the 'start' edge).
1576 return offset;
1577 }
1578
1579 NOTREACHED();
1580 return LayoutUnit();
1581 }
1582
AvailableAlignmentSpaceForChildBeforeStretching(LayoutUnit grid_area_breadth_for_child,const LayoutBox & child) const1583 LayoutUnit LayoutGrid::AvailableAlignmentSpaceForChildBeforeStretching(
1584 LayoutUnit grid_area_breadth_for_child,
1585 const LayoutBox& child) const {
1586 NOT_DESTROYED();
1587 // Because we want to avoid multiple layouts, stretching logic might be
1588 // performed before children are laid out, so we can't use the child cached
1589 // values. Hence, we may need to compute margins in order to determine the
1590 // available height before stretching.
1591 return grid_area_breadth_for_child -
1592 GridLayoutUtils::MarginLogicalHeightForChild(*this, child);
1593 }
1594
AlignSelfForChild(const LayoutBox & child,const ComputedStyle * style) const1595 StyleSelfAlignmentData LayoutGrid::AlignSelfForChild(
1596 const LayoutBox& child,
1597 const ComputedStyle* style) const {
1598 NOT_DESTROYED();
1599 if (!style)
1600 style = Style();
1601 return child.StyleRef().ResolvedAlignSelf(SelfAlignmentNormalBehavior(&child),
1602 style);
1603 }
1604
JustifySelfForChild(const LayoutBox & child,const ComputedStyle * style) const1605 StyleSelfAlignmentData LayoutGrid::JustifySelfForChild(
1606 const LayoutBox& child,
1607 const ComputedStyle* style) const {
1608 NOT_DESTROYED();
1609 if (!style)
1610 style = Style();
1611 return child.StyleRef().ResolvedJustifySelf(
1612 SelfAlignmentNormalBehavior(&child), style);
1613 }
1614
1615 // FIXME: This logic is shared by LayoutFlexibleBox, so it should be moved to
1616 // LayoutBox.
ApplyStretchAlignmentToChildIfNeeded(LayoutBox & child)1617 void LayoutGrid::ApplyStretchAlignmentToChildIfNeeded(LayoutBox& child) {
1618 NOT_DESTROYED();
1619 GridTrackSizingDirection child_block_direction =
1620 GridLayoutUtils::FlowAwareDirectionForChild(*this, child, kForRows);
1621 bool block_flow_is_column_axis = child_block_direction == kForRows;
1622 bool allowed_to_stretch_child_block_size =
1623 block_flow_is_column_axis ? AllowedToStretchChildAlongColumnAxis(child)
1624 : AllowedToStretchChildAlongRowAxis(child);
1625 if (allowed_to_stretch_child_block_size) {
1626 LayoutUnit stretched_logical_height =
1627 AvailableAlignmentSpaceForChildBeforeStretching(
1628 OverrideContainingBlockContentSizeForChild(child,
1629 child_block_direction),
1630 child);
1631 LayoutUnit desired_logical_height = child.ConstrainLogicalHeightByMinMax(
1632 stretched_logical_height, LayoutUnit(-1));
1633 child.SetOverrideLogicalHeight(desired_logical_height);
1634
1635 // Checking the logical-height of a child isn't enough. Setting an override
1636 // logical-height changes the definiteness, resulting in percentages to
1637 // resolve differently.
1638 // NG nodes have enough information to check for this case, and only layout
1639 // if needed.
1640 //
1641 // TODO (lajava): Can avoid laying out here in some cases.
1642 // See https://webkit.org/b/87905.
1643 if (desired_logical_height != child.LogicalHeight() ||
1644 child.MaybeHasPercentHeightDescendant()) {
1645 // Never mess around with the logical-height of any NG children.
1646 if (!child.IsLayoutNGMixin())
1647 child.SetLogicalHeight(LayoutUnit());
1648 child.SetSelfNeedsLayoutForAvailableSpace(true);
1649 }
1650 }
1651 }
1652
HasAutoSizeInColumnAxis(const LayoutBox & child) const1653 bool LayoutGrid::HasAutoSizeInColumnAxis(const LayoutBox& child) const {
1654 NOT_DESTROYED();
1655 if (!child.StyleRef().AspectRatio().IsAuto()) {
1656 if (IsHorizontalWritingMode() == child.IsHorizontalWritingMode()) {
1657 // If the used inline size is non-auto, we do have a non auto block size
1658 // (column axis size) because of the aspect ratio.
1659 if (!child.StyleRef().LogicalWidth().IsAuto())
1660 return false;
1661 } else {
1662 const Length& logical_height = child.StyleRef().LogicalHeight();
1663 if (logical_height.IsFixed() ||
1664 (logical_height.IsPercentOrCalc() &&
1665 child.ComputePercentageLogicalHeight(Length::Percent(0)) !=
1666 kIndefiniteSize)) {
1667 return false;
1668 }
1669 }
1670 }
1671 return IsHorizontalWritingMode() ? child.StyleRef().Height().IsAuto()
1672 : child.StyleRef().Width().IsAuto();
1673 }
1674
HasAutoSizeInRowAxis(const LayoutBox & child) const1675 bool LayoutGrid::HasAutoSizeInRowAxis(const LayoutBox& child) const {
1676 NOT_DESTROYED();
1677 if (!child.StyleRef().AspectRatio().IsAuto()) {
1678 if (IsHorizontalWritingMode() == child.IsHorizontalWritingMode()) {
1679 // If the used block size is non-auto, we do have a non auto inline size
1680 // (row axis size) because of the aspect ratio.
1681 const Length& logical_height = child.StyleRef().LogicalHeight();
1682 if (logical_height.IsFixed() ||
1683 (logical_height.IsPercentOrCalc() &&
1684 child.ComputePercentageLogicalHeight(Length::Percent(0)) !=
1685 kIndefiniteSize)) {
1686 return false;
1687 }
1688 } else {
1689 if (!child.StyleRef().LogicalWidth().IsAuto())
1690 return false;
1691 }
1692 }
1693 return IsHorizontalWritingMode() ? child.StyleRef().Width().IsAuto()
1694 : child.StyleRef().Height().IsAuto();
1695 }
1696
1697 // TODO(lajava): This logic is shared by LayoutFlexibleBox, so it should be
1698 // moved to LayoutBox.
HasAutoMarginsInColumnAxis(const LayoutBox & child) const1699 bool LayoutGrid::HasAutoMarginsInColumnAxis(const LayoutBox& child) const {
1700 NOT_DESTROYED();
1701 if (IsHorizontalWritingMode())
1702 return child.StyleRef().MarginTop().IsAuto() ||
1703 child.StyleRef().MarginBottom().IsAuto();
1704 return child.StyleRef().MarginLeft().IsAuto() ||
1705 child.StyleRef().MarginRight().IsAuto();
1706 }
1707
1708 // TODO(lajava): This logic is shared by LayoutFlexibleBox, so it should be
1709 // moved to LayoutBox.
HasAutoMarginsInRowAxis(const LayoutBox & child) const1710 bool LayoutGrid::HasAutoMarginsInRowAxis(const LayoutBox& child) const {
1711 NOT_DESTROYED();
1712 if (IsHorizontalWritingMode())
1713 return child.StyleRef().MarginLeft().IsAuto() ||
1714 child.StyleRef().MarginRight().IsAuto();
1715 return child.StyleRef().MarginTop().IsAuto() ||
1716 child.StyleRef().MarginBottom().IsAuto();
1717 }
1718
1719 // TODO(lajava): This logic is shared by LayoutFlexibleBox, so it should be
1720 // moved to LayoutBox.
1721 DISABLE_CFI_PERF
UpdateAutoMarginsInRowAxisIfNeeded(LayoutBox & child)1722 void LayoutGrid::UpdateAutoMarginsInRowAxisIfNeeded(LayoutBox& child) {
1723 NOT_DESTROYED();
1724 DCHECK(!child.IsOutOfFlowPositioned());
1725
1726 const Length& margin_start = child.StyleRef().MarginStartUsing(StyleRef());
1727 const Length& margin_end = child.StyleRef().MarginEndUsing(StyleRef());
1728 LayoutUnit margin_logical_width;
1729 // We should only consider computed margins if their specified value isn't
1730 // 'auto', since such computed value may come from a previous layout and may
1731 // be incorrect now.
1732 if (!margin_start.IsAuto())
1733 margin_logical_width += child.MarginStart();
1734 if (!margin_end.IsAuto())
1735 margin_logical_width += child.MarginEnd();
1736 LayoutUnit available_alignment_space =
1737 child.OverrideContainingBlockContentLogicalWidth() -
1738 child.LogicalWidth() - margin_logical_width;
1739 if (available_alignment_space <= 0)
1740 return;
1741
1742 if (margin_start.IsAuto() && margin_end.IsAuto()) {
1743 child.SetMarginStart(available_alignment_space / 2, Style());
1744 child.SetMarginEnd(available_alignment_space / 2, Style());
1745 } else if (margin_start.IsAuto()) {
1746 child.SetMarginStart(available_alignment_space, Style());
1747 } else if (margin_end.IsAuto()) {
1748 child.SetMarginEnd(available_alignment_space, Style());
1749 }
1750 }
1751
1752 // TODO(lajava): This logic is shared by LayoutFlexibleBox, so it should be
1753 // moved to LayoutBox.
1754 DISABLE_CFI_PERF
UpdateAutoMarginsInColumnAxisIfNeeded(LayoutBox & child)1755 void LayoutGrid::UpdateAutoMarginsInColumnAxisIfNeeded(LayoutBox& child) {
1756 NOT_DESTROYED();
1757 DCHECK(!child.IsOutOfFlowPositioned());
1758
1759 const Length& margin_before = child.StyleRef().MarginBeforeUsing(StyleRef());
1760 const Length& margin_after = child.StyleRef().MarginAfterUsing(StyleRef());
1761 LayoutUnit margin_logical_height;
1762 // We should only consider computed margins if their specified value isn't
1763 // 'auto', since such computed value may come from a previous layout and may
1764 // be incorrect now.
1765 if (!margin_before.IsAuto())
1766 margin_logical_height += child.MarginBefore();
1767 if (!margin_after.IsAuto())
1768 margin_logical_height += child.MarginAfter();
1769 LayoutUnit available_alignment_space =
1770 child.OverrideContainingBlockContentLogicalHeight() -
1771 child.LogicalHeight() - margin_logical_height;
1772 if (available_alignment_space <= 0)
1773 return;
1774
1775 if (margin_before.IsAuto() && margin_after.IsAuto()) {
1776 child.SetMarginBefore(available_alignment_space / 2, Style());
1777 child.SetMarginAfter(available_alignment_space / 2, Style());
1778 } else if (margin_before.IsAuto()) {
1779 child.SetMarginBefore(available_alignment_space, Style());
1780 } else if (margin_after.IsAuto()) {
1781 child.SetMarginAfter(available_alignment_space, Style());
1782 }
1783 }
1784
1785 // TODO(lajava): This logic is shared by LayoutFlexibleBox, so it might be
1786 // refactored somehow.
SynthesizedBaselineFromBorderBox(const LayoutBox & box,LineDirectionMode direction)1787 LayoutUnit LayoutGrid::SynthesizedBaselineFromBorderBox(
1788 const LayoutBox& box,
1789 LineDirectionMode direction) {
1790 return direction == kHorizontalLine ? box.Size().Height()
1791 : box.Size().Width();
1792 }
1793
BaselinePosition(FontBaseline,bool,LineDirectionMode direction,LinePositionMode mode) const1794 LayoutUnit LayoutGrid::BaselinePosition(FontBaseline,
1795 bool,
1796 LineDirectionMode direction,
1797 LinePositionMode mode) const {
1798 NOT_DESTROYED();
1799 DCHECK_EQ(mode, kPositionOnContainingLine);
1800 LayoutUnit baseline = FirstLineBoxBaseline();
1801 // We take border-box's bottom if no valid baseline.
1802 if (baseline == -1) {
1803 return SynthesizedBaselineFromBorderBox(*this, direction) +
1804 MarginLogicalHeight();
1805 }
1806
1807 return baseline + BeforeMarginInLineDirection(direction);
1808 }
1809
FirstLineBoxBaseline() const1810 LayoutUnit LayoutGrid::FirstLineBoxBaseline() const {
1811 NOT_DESTROYED();
1812 if (IsWritingModeRoot() || !grid_->HasGridItems() ||
1813 ShouldApplyLayoutContainment())
1814 return LayoutUnit(-1);
1815 const LayoutBox* baseline_child = nullptr;
1816 const LayoutBox* first_child = nullptr;
1817 bool is_baseline_aligned = false;
1818 // Finding the first grid item in grid order.
1819 for (size_t column = 0;
1820 !is_baseline_aligned && column < grid_->NumTracks(kForColumns);
1821 column++) {
1822 const GridItemList& cell = grid_->Cell(0, column);
1823 for (size_t index = 0; index < cell.size(); index++) {
1824 const LayoutBox* child = cell[index];
1825 DCHECK(!child->IsOutOfFlowPositioned());
1826 // If an item participates in baseline alignment, we select such item.
1827 if (IsBaselineAlignmentForChild(*child, kGridColumnAxis)) {
1828 // TODO (lajava): self-baseline and content-baseline alignment
1829 // still not implemented.
1830 baseline_child = child;
1831 is_baseline_aligned = true;
1832 break;
1833 }
1834 if (!baseline_child) {
1835 // Use dom order for items in the same cell.
1836 if (!first_child || (grid_->GridItemPaintOrder(*child) <
1837 grid_->GridItemPaintOrder(*first_child)))
1838 first_child = child;
1839 }
1840 }
1841 if (!baseline_child && first_child)
1842 baseline_child = first_child;
1843 }
1844
1845 if (!baseline_child)
1846 return LayoutUnit(-1);
1847
1848 LayoutUnit baseline =
1849 GridLayoutUtils::IsOrthogonalChild(*this, *baseline_child)
1850 ? LayoutUnit(-1)
1851 : baseline_child->FirstLineBoxBaseline();
1852 // We take border-box's bottom if no valid baseline.
1853 if (baseline == -1) {
1854 // TODO (lajava): We should pass |direction| into
1855 // firstLineBoxBaseline and stop bailing out if we're a writing
1856 // mode root. This would also fix some cases where the grid is
1857 // orthogonal to its container.
1858 LineDirectionMode direction =
1859 IsHorizontalWritingMode() ? kHorizontalLine : kVerticalLine;
1860 return SynthesizedBaselineFromBorderBox(*baseline_child, direction) +
1861 LogicalTopForChild(*baseline_child);
1862 }
1863
1864 return baseline + baseline_child->LogicalTop();
1865 }
1866
InlineBlockBaseline(LineDirectionMode direction) const1867 LayoutUnit LayoutGrid::InlineBlockBaseline(LineDirectionMode direction) const {
1868 NOT_DESTROYED();
1869 return FirstLineBoxBaseline();
1870 }
1871
IsBaselineAlignmentForChild(const LayoutBox & child) const1872 bool LayoutGrid::IsBaselineAlignmentForChild(const LayoutBox& child) const {
1873 NOT_DESTROYED();
1874 return IsBaselineAlignmentForChild(child, kGridRowAxis) ||
1875 IsBaselineAlignmentForChild(child, kGridColumnAxis);
1876 }
1877
IsBaselineAlignmentForChild(const LayoutBox & child,GridAxis baseline_axis) const1878 bool LayoutGrid::IsBaselineAlignmentForChild(const LayoutBox& child,
1879 GridAxis baseline_axis) const {
1880 NOT_DESTROYED();
1881 if (child.IsOutOfFlowPositioned())
1882 return false;
1883 ItemPosition align =
1884 SelfAlignmentForChild(baseline_axis, child).GetPosition();
1885 bool has_auto_margins = baseline_axis == kGridColumnAxis
1886 ? HasAutoMarginsInColumnAxis(child)
1887 : HasAutoMarginsInRowAxis(child);
1888 return IsBaselinePosition(align) && !has_auto_margins;
1889 }
1890
ColumnAxisBaselineOffsetForChild(const LayoutBox & child) const1891 LayoutUnit LayoutGrid::ColumnAxisBaselineOffsetForChild(
1892 const LayoutBox& child) const {
1893 NOT_DESTROYED();
1894 return track_sizing_algorithm_.BaselineOffsetForChild(child, kGridColumnAxis);
1895 }
1896
RowAxisBaselineOffsetForChild(const LayoutBox & child) const1897 LayoutUnit LayoutGrid::RowAxisBaselineOffsetForChild(
1898 const LayoutBox& child) const {
1899 NOT_DESTROYED();
1900 return track_sizing_algorithm_.BaselineOffsetForChild(child, kGridRowAxis);
1901 }
1902
ColumnAxisPositionForChild(const LayoutBox & child) const1903 GridAxisPosition LayoutGrid::ColumnAxisPositionForChild(
1904 const LayoutBox& child) const {
1905 NOT_DESTROYED();
1906 bool has_same_writing_mode =
1907 child.StyleRef().GetWritingMode() == StyleRef().GetWritingMode();
1908 bool child_is_ltr = child.StyleRef().IsLeftToRightDirection();
1909 if (child.IsOutOfFlowPositioned() &&
1910 !HasStaticPositionForChild(child, kForRows))
1911 return kGridAxisStart;
1912
1913 switch (AlignSelfForChild(child).GetPosition()) {
1914 case ItemPosition::kSelfStart:
1915 // TODO (lajava): Should we implement this logic in a generic utility
1916 // function?
1917 // Aligns the alignment subject to be flush with the edge of the alignment
1918 // container corresponding to the alignment subject's 'start' side in the
1919 // column axis.
1920 if (GridLayoutUtils::IsOrthogonalChild(*this, child)) {
1921 // If orthogonal writing-modes, self-start will be based on the child's
1922 // inline-axis direction (inline-start), because it's the one parallel
1923 // to the column axis.
1924 if (StyleRef().IsFlippedBlocksWritingMode())
1925 return child_is_ltr ? kGridAxisEnd : kGridAxisStart;
1926 return child_is_ltr ? kGridAxisStart : kGridAxisEnd;
1927 }
1928 // self-start is based on the child's block-flow direction. That's why we
1929 // need to check against the grid container's block-flow direction.
1930 return has_same_writing_mode ? kGridAxisStart : kGridAxisEnd;
1931 case ItemPosition::kSelfEnd:
1932 // TODO (lajava): Should we implement this logic in a generic utility
1933 // function?
1934 // Aligns the alignment subject to be flush with the edge of the alignment
1935 // container corresponding to the alignment subject's 'end' side in the
1936 // column axis.
1937 if (GridLayoutUtils::IsOrthogonalChild(*this, child)) {
1938 // If orthogonal writing-modes, self-end will be based on the child's
1939 // inline-axis direction, (inline-end) because it's the one parallel to
1940 // the column axis.
1941 if (StyleRef().IsFlippedBlocksWritingMode())
1942 return child_is_ltr ? kGridAxisStart : kGridAxisEnd;
1943 return child_is_ltr ? kGridAxisEnd : kGridAxisStart;
1944 }
1945 // self-end is based on the child's block-flow direction. That's why we
1946 // need to check against the grid container's block-flow direction.
1947 return has_same_writing_mode ? kGridAxisEnd : kGridAxisStart;
1948 case ItemPosition::kCenter:
1949 return kGridAxisCenter;
1950 // Only used in flex layout, otherwise equivalent to 'start'.
1951 case ItemPosition::kFlexStart:
1952 // Aligns the alignment subject to be flush with the alignment container's
1953 // 'start' edge (block-start) in the column axis.
1954 case ItemPosition::kStart:
1955 return kGridAxisStart;
1956 // Only used in flex layout, otherwise equivalent to 'end'.
1957 case ItemPosition::kFlexEnd:
1958 // Aligns the alignment subject to be flush with the alignment container's
1959 // 'end' edge (block-end) in the column axis.
1960 case ItemPosition::kEnd:
1961 return kGridAxisEnd;
1962 case ItemPosition::kStretch:
1963 return kGridAxisStart;
1964 case ItemPosition::kBaseline:
1965 case ItemPosition::kLastBaseline:
1966 return kGridAxisStart;
1967 case ItemPosition::kLegacy:
1968 case ItemPosition::kAuto:
1969 case ItemPosition::kNormal:
1970 case ItemPosition::kLeft:
1971 case ItemPosition::kRight:
1972 break;
1973 }
1974
1975 NOTREACHED();
1976 return kGridAxisStart;
1977 }
1978
RowAxisPositionForChild(const LayoutBox & child) const1979 GridAxisPosition LayoutGrid::RowAxisPositionForChild(
1980 const LayoutBox& child) const {
1981 NOT_DESTROYED();
1982 bool has_same_direction =
1983 child.StyleRef().Direction() == StyleRef().Direction();
1984 bool grid_is_ltr = StyleRef().IsLeftToRightDirection();
1985 if (child.IsOutOfFlowPositioned() &&
1986 !HasStaticPositionForChild(child, kForColumns))
1987 return kGridAxisStart;
1988
1989 switch (JustifySelfForChild(child).GetPosition()) {
1990 case ItemPosition::kSelfStart:
1991 // TODO (lajava): Should we implement this logic in a generic utility
1992 // function?
1993 // Aligns the alignment subject to be flush with the edge of the alignment
1994 // container corresponding to the alignment subject's 'start' side in the
1995 // row axis.
1996 if (GridLayoutUtils::IsOrthogonalChild(*this, child)) {
1997 // If orthogonal writing-modes, self-start will be based on the child's
1998 // block-axis direction, because it's the one parallel to the row axis.
1999 if (child.StyleRef().IsFlippedBlocksWritingMode())
2000 return grid_is_ltr ? kGridAxisEnd : kGridAxisStart;
2001 return grid_is_ltr ? kGridAxisStart : kGridAxisEnd;
2002 }
2003 // self-start is based on the child's inline-flow direction. That's why we
2004 // need to check against the grid container's direction.
2005 return has_same_direction ? kGridAxisStart : kGridAxisEnd;
2006 case ItemPosition::kSelfEnd:
2007 // TODO (lajava): Should we implement this logic in a generic utility
2008 // function?
2009 // Aligns the alignment subject to be flush with the edge of the alignment
2010 // container corresponding to the alignment subject's 'end' side in the
2011 // row axis.
2012 if (GridLayoutUtils::IsOrthogonalChild(*this, child)) {
2013 // If orthogonal writing-modes, self-end will be based on the child's
2014 // block-axis direction, because it's the one parallel to the row axis.
2015 if (child.StyleRef().IsFlippedBlocksWritingMode())
2016 return grid_is_ltr ? kGridAxisStart : kGridAxisEnd;
2017 return grid_is_ltr ? kGridAxisEnd : kGridAxisStart;
2018 }
2019 // self-end is based on the child's inline-flow direction. That's why we
2020 // need to check against the grid container's direction.
2021 return has_same_direction ? kGridAxisEnd : kGridAxisStart;
2022 case ItemPosition::kLeft:
2023 // Aligns the alignment subject to be flush with the alignment container's
2024 // 'line-left' edge. We want the physical 'left' side, so we have to take
2025 // account, container's inline-flow direction.
2026 return grid_is_ltr ? kGridAxisStart : kGridAxisEnd;
2027 case ItemPosition::kRight:
2028 // Aligns the alignment subject to be flush with the alignment container's
2029 // 'line-right' edge. We want the physical 'right' side, so we have to
2030 // take account, container's inline-flow direction.
2031 return grid_is_ltr ? kGridAxisEnd : kGridAxisStart;
2032 case ItemPosition::kCenter:
2033 return kGridAxisCenter;
2034 // Only used in flex layout, otherwise equivalent to 'start'.
2035 case ItemPosition::kFlexStart:
2036 // Aligns the alignment subject to be flush with the alignment container's
2037 // 'start' edge (inline-start) in the row axis.
2038 case ItemPosition::kStart:
2039 return kGridAxisStart;
2040 // Only used in flex layout, otherwise equivalent to 'end'.
2041 case ItemPosition::kFlexEnd:
2042 // Aligns the alignment subject to be flush with the alignment container's
2043 // 'end' edge (inline-end) in the row axis.
2044 case ItemPosition::kEnd:
2045 return kGridAxisEnd;
2046 case ItemPosition::kStretch:
2047 return kGridAxisStart;
2048 case ItemPosition::kBaseline:
2049 case ItemPosition::kLastBaseline:
2050 return kGridAxisStart;
2051 case ItemPosition::kLegacy:
2052 case ItemPosition::kAuto:
2053 case ItemPosition::kNormal:
2054 break;
2055 }
2056
2057 NOTREACHED();
2058 return kGridAxisStart;
2059 }
2060
ColumnAxisOffsetForChild(const LayoutBox & child) const2061 LayoutUnit LayoutGrid::ColumnAxisOffsetForChild(const LayoutBox& child) const {
2062 NOT_DESTROYED();
2063 LayoutUnit start_of_row;
2064 LayoutUnit end_of_row;
2065 GridAreaPositionForChild(child, kForRows, start_of_row, end_of_row);
2066 LayoutUnit start_position = start_of_row + MarginBeforeForChild(child);
2067 if (HasAutoMarginsInColumnAxis(child))
2068 return start_position;
2069 GridAxisPosition axis_position = ColumnAxisPositionForChild(child);
2070 switch (axis_position) {
2071 case kGridAxisStart:
2072 return start_position + ColumnAxisBaselineOffsetForChild(child);
2073 case kGridAxisEnd:
2074 case kGridAxisCenter: {
2075 LayoutUnit column_axis_child_size =
2076 GridLayoutUtils::IsOrthogonalChild(*this, child)
2077 ? child.LogicalWidth() + child.MarginLogicalWidth()
2078 : child.LogicalHeight() + child.MarginLogicalHeight();
2079 OverflowAlignment overflow = AlignSelfForChild(child).Overflow();
2080 LayoutUnit offset_from_start_position = ComputeOverflowAlignmentOffset(
2081 overflow, end_of_row - start_of_row, column_axis_child_size);
2082 return start_position + (axis_position == kGridAxisEnd
2083 ? offset_from_start_position
2084 : offset_from_start_position / 2);
2085 }
2086 }
2087
2088 NOTREACHED();
2089 return LayoutUnit();
2090 }
2091
RowAxisOffsetForChild(const LayoutBox & child) const2092 LayoutUnit LayoutGrid::RowAxisOffsetForChild(const LayoutBox& child) const {
2093 NOT_DESTROYED();
2094 LayoutUnit start_of_column;
2095 LayoutUnit end_of_column;
2096 GridAreaPositionForChild(child, kForColumns, start_of_column, end_of_column);
2097 LayoutUnit start_position = start_of_column + MarginStartForChild(child);
2098 if (HasAutoMarginsInRowAxis(child))
2099 return start_position;
2100 GridAxisPosition axis_position = RowAxisPositionForChild(child);
2101 switch (axis_position) {
2102 case kGridAxisStart:
2103 return start_position + RowAxisBaselineOffsetForChild(child);
2104 case kGridAxisEnd:
2105 case kGridAxisCenter: {
2106 LayoutUnit row_axis_child_size =
2107 GridLayoutUtils::IsOrthogonalChild(*this, child)
2108 ? child.LogicalHeight() + child.MarginLogicalHeight()
2109 : child.LogicalWidth() + child.MarginLogicalWidth();
2110 OverflowAlignment overflow = JustifySelfForChild(child).Overflow();
2111 LayoutUnit offset_from_start_position = ComputeOverflowAlignmentOffset(
2112 overflow, end_of_column - start_of_column, row_axis_child_size);
2113 return start_position + (axis_position == kGridAxisEnd
2114 ? offset_from_start_position
2115 : offset_from_start_position / 2);
2116 }
2117 }
2118
2119 NOTREACHED();
2120 return LayoutUnit();
2121 }
2122
ResolveAutoStartGridPosition(GridTrackSizingDirection direction) const2123 LayoutUnit LayoutGrid::ResolveAutoStartGridPosition(
2124 GridTrackSizingDirection direction) const {
2125 NOT_DESTROYED();
2126 if (direction == kForRows || StyleRef().IsLeftToRightDirection())
2127 return LayoutUnit();
2128
2129 int last_line = NumTracks(kForColumns, *grid_);
2130 ContentPosition position = StyleRef().ResolvedJustifyContentPosition(
2131 ContentAlignmentNormalBehavior());
2132 if (position == ContentPosition::kEnd)
2133 return column_positions_[last_line] - ClientLogicalWidth();
2134 if (position == ContentPosition::kStart ||
2135 StyleRef().ResolvedJustifyContentDistribution(
2136 ContentAlignmentNormalBehavior()) ==
2137 ContentDistributionType::kStretch)
2138 return column_positions_[0] - BorderAndPaddingLogicalLeft();
2139 return LayoutUnit();
2140 }
2141
ResolveAutoEndGridPosition(GridTrackSizingDirection direction) const2142 LayoutUnit LayoutGrid::ResolveAutoEndGridPosition(
2143 GridTrackSizingDirection direction) const {
2144 NOT_DESTROYED();
2145 if (direction == kForRows)
2146 return ClientLogicalHeight();
2147 if (StyleRef().IsLeftToRightDirection())
2148 return ClientLogicalWidth();
2149
2150 int last_line = NumTracks(kForColumns, *grid_);
2151 ContentPosition position = StyleRef().ResolvedJustifyContentPosition(
2152 ContentAlignmentNormalBehavior());
2153 if (position == ContentPosition::kEnd)
2154 return column_positions_[last_line];
2155 if (position == ContentPosition::kStart ||
2156 StyleRef().ResolvedJustifyContentDistribution(
2157 ContentAlignmentNormalBehavior()) ==
2158 ContentDistributionType::kStretch) {
2159 return column_positions_[0] - BorderAndPaddingLogicalLeft() +
2160 ClientLogicalWidth();
2161 }
2162 return ClientLogicalWidth();
2163 }
2164
GridAreaBreadthForOutOfFlowChild(const LayoutBox & child,GridTrackSizingDirection direction)2165 LayoutUnit LayoutGrid::GridAreaBreadthForOutOfFlowChild(
2166 const LayoutBox& child,
2167 GridTrackSizingDirection direction) {
2168 NOT_DESTROYED();
2169 DCHECK(child.IsOutOfFlowPositioned());
2170 bool is_row_axis = direction == kForColumns;
2171 GridSpan span = GridPositionsResolver::ResolveGridPositionsFromStyle(
2172 StyleRef(), child.StyleRef(), direction,
2173 AutoRepeatCountForDirection(direction));
2174 if (span.IsIndefinite())
2175 return is_row_axis ? ClientLogicalWidth() : ClientLogicalHeight();
2176
2177 size_t explicit_start = grid_->ExplicitGridStart(direction);
2178 size_t start_line = span.UntranslatedStartLine() + explicit_start;
2179 size_t end_line = span.UntranslatedEndLine() + explicit_start;
2180 size_t last_line = NumTracks(direction, *grid_);
2181 GridPosition start_position = direction == kForColumns
2182 ? child.StyleRef().GridColumnStart()
2183 : child.StyleRef().GridRowStart();
2184 GridPosition end_position = direction == kForColumns
2185 ? child.StyleRef().GridColumnEnd()
2186 : child.StyleRef().GridRowEnd();
2187
2188 bool start_is_auto =
2189 start_position.IsAuto() || start_line < 0 || start_line > last_line;
2190 bool end_is_auto =
2191 end_position.IsAuto() || end_line < 0 || end_line > last_line;
2192
2193 if (start_is_auto && end_is_auto)
2194 return is_row_axis ? ClientLogicalWidth() : ClientLogicalHeight();
2195
2196 LayoutUnit start;
2197 LayoutUnit end;
2198 auto& positions = is_row_axis ? column_positions_ : row_positions_;
2199 auto& line_of_positioned_item =
2200 is_row_axis ? column_of_positioned_item_ : row_of_positioned_item_;
2201 LayoutUnit border_edge = is_row_axis ? BorderLogicalLeft() : BorderBefore();
2202 if (start_is_auto) {
2203 start = ResolveAutoStartGridPosition(direction) + border_edge;
2204 } else {
2205 line_of_positioned_item.Set(&child, start_line);
2206 start = positions[start_line];
2207 }
2208 if (end_is_auto) {
2209 end = ResolveAutoEndGridPosition(direction) + border_edge;
2210 } else {
2211 end = positions[end_line];
2212 // These vectors store line positions including gaps, but we shouldn't
2213 // consider them for the edges of the grid.
2214 if (end_line > 0 && end_line < last_line) {
2215 DCHECK(!grid_->NeedsItemsPlacement());
2216 // TODO(rego): It would be more efficient to call GridGap(direction) and
2217 // pass that value to GuttersSize(), so we could avoid the call to
2218 // available size if the gutter doesn't use percentages.
2219 end -= GuttersSize(
2220 *grid_, direction, end_line - 1, 2,
2221 is_row_axis ? AvailableLogicalWidth() : ContentLogicalHeight());
2222 end -= is_row_axis ? offset_between_columns_.distribution_offset
2223 : offset_between_rows_.distribution_offset;
2224 }
2225 }
2226 // TODO (lajava): Is expectable that in some cases 'end' is smaller than
2227 // 'start' ?
2228 return std::max(end - start, LayoutUnit());
2229 }
2230
LogicalOffsetForOutOfFlowChild(const LayoutBox & child,GridTrackSizingDirection direction,LayoutUnit track_breadth) const2231 LayoutUnit LayoutGrid::LogicalOffsetForOutOfFlowChild(
2232 const LayoutBox& child,
2233 GridTrackSizingDirection direction,
2234 LayoutUnit track_breadth) const {
2235 NOT_DESTROYED();
2236 DCHECK(child.IsOutOfFlowPositioned());
2237 if (HasStaticPositionForChild(child, direction))
2238 return LayoutUnit();
2239
2240 bool is_row_axis = direction == kForColumns;
2241 bool is_flowaware_row_axis = GridLayoutUtils::FlowAwareDirectionForChild(
2242 *this, child, direction) == kForColumns;
2243 LayoutUnit child_position =
2244 is_flowaware_row_axis ? child.LogicalLeft() : child.LogicalTop();
2245 LayoutUnit grid_border = is_row_axis ? BorderLogicalLeft() : BorderBefore();
2246 LayoutUnit child_margin =
2247 is_flowaware_row_axis ? child.MarginLineLeft() : child.MarginBefore();
2248 LayoutUnit offset = child_position - grid_border - child_margin;
2249 if (!is_row_axis || StyleRef().IsLeftToRightDirection())
2250 return offset;
2251
2252 LayoutUnit child_breadth =
2253 is_flowaware_row_axis
2254 ? child.LogicalWidth() + child.MarginLogicalWidth()
2255 : child.LogicalHeight() + child.MarginLogicalHeight();
2256 return track_breadth - offset - child_breadth;
2257 }
2258
GridAreaPositionForOutOfFlowChild(const LayoutBox & child,GridTrackSizingDirection direction,LayoutUnit & start,LayoutUnit & end) const2259 void LayoutGrid::GridAreaPositionForOutOfFlowChild(
2260 const LayoutBox& child,
2261 GridTrackSizingDirection direction,
2262 LayoutUnit& start,
2263 LayoutUnit& end) const {
2264 NOT_DESTROYED();
2265 DCHECK(child.IsOutOfFlowPositioned());
2266 DCHECK(GridLayoutUtils::HasOverrideContainingBlockContentSizeForChild(
2267 child, direction));
2268 LayoutUnit track_breadth =
2269 GridLayoutUtils::OverrideContainingBlockContentSizeForChild(child,
2270 direction);
2271 bool is_row_axis = direction == kForColumns;
2272 auto& line_of_positioned_item =
2273 is_row_axis ? column_of_positioned_item_ : row_of_positioned_item_;
2274 start = is_row_axis ? BorderLogicalLeft() : BorderBefore();
2275 if (base::Optional<size_t> line = line_of_positioned_item.at(&child)) {
2276 auto& positions = is_row_axis ? column_positions_ : row_positions_;
2277 start = positions[line.value()];
2278 }
2279 start += LogicalOffsetForOutOfFlowChild(child, direction, track_breadth);
2280 end = start + track_breadth;
2281 }
2282
GridAreaPositionForInFlowChild(const LayoutBox & child,GridTrackSizingDirection direction,LayoutUnit & start,LayoutUnit & end) const2283 void LayoutGrid::GridAreaPositionForInFlowChild(
2284 const LayoutBox& child,
2285 GridTrackSizingDirection direction,
2286 LayoutUnit& start,
2287 LayoutUnit& end) const {
2288 NOT_DESTROYED();
2289 DCHECK(!child.IsOutOfFlowPositioned());
2290 const Grid& grid = track_sizing_algorithm_.GetGrid();
2291 const GridSpan& span = grid.GridItemSpan(child, direction);
2292 // TODO (lajava): This is a common pattern, why not defining a function like
2293 // positions(direction) ?
2294 auto& positions =
2295 direction == kForColumns ? column_positions_ : row_positions_;
2296 start = positions[span.StartLine()];
2297 end = positions[span.EndLine()];
2298 // The 'positions' vector includes distribution offset (because of content
2299 // alignment) and gutters so we need to subtract them to get the actual
2300 // end position for a given track (this does not have to be done for the
2301 // last track as there are no more positions's elements after it, nor for
2302 // collapsed tracks).
2303 if (span.EndLine() < positions.size() - 1 &&
2304 !(grid.HasAutoRepeatEmptyTracks(direction) &&
2305 grid.IsEmptyAutoRepeatTrack(direction, span.EndLine())))
2306 end -= GridGap(direction) + GridItemOffset(direction);
2307 }
2308
GridAreaPositionForChild(const LayoutBox & child,GridTrackSizingDirection direction,LayoutUnit & start,LayoutUnit & end) const2309 void LayoutGrid::GridAreaPositionForChild(const LayoutBox& child,
2310 GridTrackSizingDirection direction,
2311 LayoutUnit& start,
2312 LayoutUnit& end) const {
2313 NOT_DESTROYED();
2314 if (child.IsOutOfFlowPositioned())
2315 GridAreaPositionForOutOfFlowChild(child, direction, start, end);
2316 else
2317 GridAreaPositionForInFlowChild(child, direction, start, end);
2318 }
2319
ResolveContentDistributionFallback(ContentDistributionType distribution)2320 ContentPosition static ResolveContentDistributionFallback(
2321 ContentDistributionType distribution) {
2322 switch (distribution) {
2323 case ContentDistributionType::kSpaceBetween:
2324 return ContentPosition::kStart;
2325 case ContentDistributionType::kSpaceAround:
2326 return ContentPosition::kCenter;
2327 case ContentDistributionType::kSpaceEvenly:
2328 return ContentPosition::kCenter;
2329 case ContentDistributionType::kStretch:
2330 return ContentPosition::kStart;
2331 case ContentDistributionType::kDefault:
2332 return ContentPosition::kNormal;
2333 }
2334
2335 NOTREACHED();
2336 return ContentPosition::kNormal;
2337 }
2338
ComputeContentDistributionOffset(ContentAlignmentData & offset,const LayoutUnit & available_free_space,ContentPosition & fallback_position,ContentDistributionType distribution,unsigned number_of_grid_tracks)2339 static void ComputeContentDistributionOffset(
2340 ContentAlignmentData& offset,
2341 const LayoutUnit& available_free_space,
2342 ContentPosition& fallback_position,
2343 ContentDistributionType distribution,
2344 unsigned number_of_grid_tracks) {
2345 if (distribution != ContentDistributionType::kDefault &&
2346 fallback_position == ContentPosition::kNormal)
2347 fallback_position = ResolveContentDistributionFallback(distribution);
2348
2349 // Initialize to an invalid offset.
2350 offset.position_offset = LayoutUnit(-1);
2351 offset.distribution_offset = LayoutUnit(-1);
2352 if (available_free_space <= 0)
2353 return;
2354
2355 LayoutUnit position_offset;
2356 LayoutUnit distribution_offset;
2357 switch (distribution) {
2358 case ContentDistributionType::kSpaceBetween:
2359 if (number_of_grid_tracks < 2)
2360 return;
2361 distribution_offset = available_free_space / (number_of_grid_tracks - 1);
2362 position_offset = LayoutUnit();
2363 break;
2364 case ContentDistributionType::kSpaceAround:
2365 if (number_of_grid_tracks < 1)
2366 return;
2367 distribution_offset = available_free_space / number_of_grid_tracks;
2368 position_offset = distribution_offset / 2;
2369 break;
2370 case ContentDistributionType::kSpaceEvenly:
2371 distribution_offset = available_free_space / (number_of_grid_tracks + 1);
2372 position_offset = distribution_offset;
2373 break;
2374 case ContentDistributionType::kStretch:
2375 case ContentDistributionType::kDefault:
2376 return;
2377 default:
2378 NOTREACHED();
2379 return;
2380 }
2381
2382 offset.position_offset = position_offset;
2383 offset.distribution_offset = distribution_offset;
2384 }
2385
ContentAlignment(GridTrackSizingDirection direction) const2386 StyleContentAlignmentData LayoutGrid::ContentAlignment(
2387 GridTrackSizingDirection direction) const {
2388 NOT_DESTROYED();
2389 return direction == kForColumns ? StyleRef().ResolvedJustifyContent(
2390 ContentAlignmentNormalBehavior())
2391 : StyleRef().ResolvedAlignContent(
2392 ContentAlignmentNormalBehavior());
2393 }
2394
ComputeContentPositionAndDistributionOffset(GridTrackSizingDirection direction,const LayoutUnit & available_free_space,unsigned number_of_grid_tracks)2395 void LayoutGrid::ComputeContentPositionAndDistributionOffset(
2396 GridTrackSizingDirection direction,
2397 const LayoutUnit& available_free_space,
2398 unsigned number_of_grid_tracks) {
2399 NOT_DESTROYED();
2400 auto& offset =
2401 direction == kForColumns ? offset_between_columns_ : offset_between_rows_;
2402 StyleContentAlignmentData content_alignment_data =
2403 ContentAlignment(direction);
2404 ContentPosition position = content_alignment_data.GetPosition();
2405 // If <content-distribution> value can't be applied, 'position' will become
2406 // the associated <content-position> fallback value.
2407 ComputeContentDistributionOffset(offset, available_free_space, position,
2408 content_alignment_data.Distribution(),
2409 number_of_grid_tracks);
2410 if (offset.IsValid())
2411 return;
2412
2413 // TODO (lajava): Default value for overflow isn't exaclty as 'unsafe'.
2414 // https://drafts.csswg.org/css-align/#overflow-values
2415 if (available_free_space == 0 ||
2416 (available_free_space < 0 &&
2417 content_alignment_data.Overflow() == OverflowAlignment::kSafe)) {
2418 offset.position_offset = LayoutUnit();
2419 offset.distribution_offset = LayoutUnit();
2420 return;
2421 }
2422
2423 LayoutUnit position_offset;
2424 bool is_row_axis = direction == kForColumns;
2425 switch (position) {
2426 case ContentPosition::kLeft:
2427 DCHECK(is_row_axis);
2428 position_offset = LayoutUnit();
2429 break;
2430 case ContentPosition::kRight:
2431 DCHECK(is_row_axis);
2432 position_offset = available_free_space;
2433 break;
2434 case ContentPosition::kCenter:
2435 position_offset = available_free_space / 2;
2436 break;
2437 // Only used in flex layout, for other layout, it's equivalent to 'End'.
2438 case ContentPosition::kFlexEnd:
2439 U_FALLTHROUGH;
2440 case ContentPosition::kEnd:
2441 if (is_row_axis) {
2442 position_offset = StyleRef().IsLeftToRightDirection()
2443 ? available_free_space
2444 : LayoutUnit();
2445 } else {
2446 position_offset = available_free_space;
2447 }
2448 break;
2449 // Only used in flex layout, for other layout, it's equivalent to 'Start'.
2450 case ContentPosition::kFlexStart:
2451 U_FALLTHROUGH;
2452 case ContentPosition::kStart:
2453 if (is_row_axis) {
2454 position_offset = StyleRef().IsLeftToRightDirection()
2455 ? LayoutUnit()
2456 : available_free_space;
2457 } else {
2458 position_offset = LayoutUnit();
2459 }
2460 break;
2461 case ContentPosition::kBaseline:
2462 U_FALLTHROUGH;
2463 case ContentPosition::kLastBaseline:
2464 // FIXME: These two require implementing Baseline Alignment. For now, we
2465 // always 'start' align the child. crbug.com/234191
2466 if (is_row_axis) {
2467 position_offset = StyleRef().IsLeftToRightDirection()
2468 ? LayoutUnit()
2469 : available_free_space;
2470 } else {
2471 position_offset = LayoutUnit();
2472 }
2473 break;
2474 case ContentPosition::kNormal:
2475 U_FALLTHROUGH;
2476 default:
2477 NOTREACHED();
2478 return;
2479 }
2480
2481 offset.position_offset = position_offset;
2482 offset.distribution_offset = LayoutUnit();
2483 }
2484
TranslateOutOfFlowRTLCoordinate(const LayoutBox & child,LayoutUnit coordinate) const2485 LayoutUnit LayoutGrid::TranslateOutOfFlowRTLCoordinate(
2486 const LayoutBox& child,
2487 LayoutUnit coordinate) const {
2488 NOT_DESTROYED();
2489 DCHECK(child.IsOutOfFlowPositioned());
2490 DCHECK(!StyleRef().IsLeftToRightDirection());
2491
2492 if (column_of_positioned_item_.at(&child))
2493 return TranslateRTLCoordinate(coordinate);
2494
2495 return BorderLogicalLeft() + BorderLogicalRight() + ClientLogicalWidth() -
2496 coordinate;
2497 }
2498
TranslateRTLCoordinate(LayoutUnit coordinate) const2499 LayoutUnit LayoutGrid::TranslateRTLCoordinate(LayoutUnit coordinate) const {
2500 NOT_DESTROYED();
2501 DCHECK(!StyleRef().IsLeftToRightDirection());
2502
2503 LayoutUnit alignment_offset = column_positions_[0];
2504 LayoutUnit right_grid_edge_position =
2505 column_positions_[column_positions_.size() - 1];
2506 return right_grid_edge_position + alignment_offset - coordinate;
2507 }
2508
2509 // TODO: SetLogicalPositionForChild has only one caller, consider its
2510 // refactoring in the future.
SetLogicalPositionForChild(LayoutBox & child) const2511 void LayoutGrid::SetLogicalPositionForChild(LayoutBox& child) const {
2512 NOT_DESTROYED();
2513 // "In the positioning phase [...] calculations are performed according to the
2514 // writing mode of the containing block of the box establishing the orthogonal
2515 // flow." However, 'setLogicalPosition' will only take into account the
2516 // child's writing-mode, so the position may need to be transposed.
2517 LayoutPoint child_location(LogicalOffsetForChild(child, kForColumns),
2518 LogicalOffsetForChild(child, kForRows));
2519 child.SetLogicalLocation(GridLayoutUtils::IsOrthogonalChild(*this, child)
2520 ? child_location.TransposedPoint()
2521 : child_location);
2522 }
2523
SetLogicalOffsetForChild(LayoutBox & child,GridTrackSizingDirection direction) const2524 void LayoutGrid::SetLogicalOffsetForChild(
2525 LayoutBox& child,
2526 GridTrackSizingDirection direction) const {
2527 NOT_DESTROYED();
2528 if (!child.IsGridItem() && HasStaticPositionForChild(child, direction))
2529 return;
2530 // 'SetLogicalLeft' and 'SetLogicalTop' only take into account the child's
2531 // writing-mode, that's why 'FlowAwareDirectionForChild' is needed.
2532 if (GridLayoutUtils::FlowAwareDirectionForChild(*this, child, direction) ==
2533 kForColumns)
2534 child.SetLogicalLeft(LogicalOffsetForChild(child, direction));
2535 else
2536 child.SetLogicalTop(LogicalOffsetForChild(child, direction));
2537 }
2538
LogicalOffsetForChild(const LayoutBox & child,GridTrackSizingDirection direction) const2539 LayoutUnit LayoutGrid::LogicalOffsetForChild(
2540 const LayoutBox& child,
2541 GridTrackSizingDirection direction) const {
2542 NOT_DESTROYED();
2543 if (direction == kForRows) {
2544 return ColumnAxisOffsetForChild(child);
2545 }
2546 LayoutUnit row_axis_offset = RowAxisOffsetForChild(child);
2547 // We stored column_position_'s data ignoring the direction, hence we might
2548 // need now to translate positions from RTL to LTR, as it's more convenient
2549 // for painting.
2550 if (!StyleRef().IsLeftToRightDirection()) {
2551 row_axis_offset =
2552 (child.IsOutOfFlowPositioned()
2553 ? TranslateOutOfFlowRTLCoordinate(child, row_axis_offset)
2554 : TranslateRTLCoordinate(row_axis_offset)) -
2555 (GridLayoutUtils::IsOrthogonalChild(*this, child)
2556 ? child.LogicalHeight()
2557 : child.LogicalWidth());
2558 }
2559 return row_axis_offset;
2560 }
2561
GridAreaLogicalPosition(const GridArea & area) const2562 LayoutPoint LayoutGrid::GridAreaLogicalPosition(const GridArea& area) const {
2563 NOT_DESTROYED();
2564 LayoutUnit column_axis_offset = row_positions_[area.rows.StartLine()];
2565 LayoutUnit row_axis_offset = column_positions_[area.columns.StartLine()];
2566
2567 // See comment in findChildLogicalPosition() about why we need sometimes to
2568 // translate from RTL to LTR the rowAxisOffset coordinate.
2569 return LayoutPoint(StyleRef().IsLeftToRightDirection()
2570 ? row_axis_offset
2571 : TranslateRTLCoordinate(row_axis_offset),
2572 column_axis_offset);
2573 }
2574
PaintChildren(const PaintInfo & paint_info,const PhysicalOffset & paint_offset) const2575 void LayoutGrid::PaintChildren(const PaintInfo& paint_info,
2576 const PhysicalOffset& paint_offset) const {
2577 NOT_DESTROYED();
2578 DCHECK(!grid_->NeedsItemsPlacement());
2579 if (grid_->HasGridItems()) {
2580 BlockPainter(*this).PaintChildrenAtomically(grid_->GetOrderIterator(),
2581 paint_info);
2582 }
2583 }
2584
CachedHasDefiniteLogicalHeight() const2585 bool LayoutGrid::CachedHasDefiniteLogicalHeight() const {
2586 NOT_DESTROYED();
2587 SECURITY_DCHECK(has_definite_logical_height_);
2588 return has_definite_logical_height_.value();
2589 }
2590
NonCollapsedTracks(GridTrackSizingDirection direction) const2591 size_t LayoutGrid::NonCollapsedTracks(
2592 GridTrackSizingDirection direction) const {
2593 NOT_DESTROYED();
2594 auto& tracks = track_sizing_algorithm_.Tracks(direction);
2595 size_t number_of_tracks = tracks.size();
2596 bool has_collapsed_tracks = grid_->HasAutoRepeatEmptyTracks(direction);
2597 size_t number_of_collapsed_tracks =
2598 has_collapsed_tracks ? grid_->AutoRepeatEmptyTracks(direction)->size()
2599 : 0;
2600 return number_of_tracks - number_of_collapsed_tracks;
2601 }
2602
NumTracks(GridTrackSizingDirection direction,const Grid & grid) const2603 size_t LayoutGrid::NumTracks(GridTrackSizingDirection direction,
2604 const Grid& grid) const {
2605 NOT_DESTROYED();
2606 // Due to limitations in our internal representation, we cannot know the
2607 // number of columns from m_grid *if* there is no row (because m_grid would be
2608 // empty). That's why in that case we need to get it from the style. Note that
2609 // we know for sure that there are't any implicit tracks, because not having
2610 // rows implies that there are no "normal" children (out-of-flow children are
2611 // not stored in m_grid).
2612 DCHECK(!grid.NeedsItemsPlacement());
2613 if (direction == kForRows)
2614 return grid.NumTracks(kForRows);
2615
2616 return grid.NumTracks(kForRows)
2617 ? grid.NumTracks(kForColumns)
2618 : GridPositionsResolver::ExplicitGridColumnCount(
2619 StyleRef(), grid.AutoRepeatTracks(kForColumns));
2620 }
2621
ExplicitGridEndForDirection(GridTrackSizingDirection direction) const2622 size_t LayoutGrid::ExplicitGridEndForDirection(
2623 GridTrackSizingDirection direction) const {
2624 NOT_DESTROYED();
2625 size_t leading = ExplicitGridStartForDirection(direction);
2626
2627 if (direction == kForRows) {
2628 return leading + GridPositionsResolver::ExplicitGridRowCount(
2629 StyleRef(), grid_->AutoRepeatTracks(direction));
2630 }
2631
2632 return leading + GridPositionsResolver::ExplicitGridColumnCount(
2633 StyleRef(), grid_->AutoRepeatTracks(direction));
2634 }
2635
GridItemOffset(GridTrackSizingDirection direction) const2636 LayoutUnit LayoutGrid::GridItemOffset(
2637 GridTrackSizingDirection direction) const {
2638 NOT_DESTROYED();
2639 return direction == kForRows ? offset_between_rows_.distribution_offset
2640 : offset_between_columns_.distribution_offset;
2641 }
2642
2643 } // namespace blink
2644