// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_LAYOUT_ALGORITHM_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_LAYOUT_ALGORITHM_H_ #include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h" #include "third_party/blink/renderer/platform/wtf/vector.h" namespace blink { class NGGridPlacement; class CORE_EXPORT NGGridLayoutAlgorithm : public NGLayoutAlgorithm { public: enum class AutoPlacementType { kNotNeeded, kMajor, kMinor, kBoth }; enum class AxisEdge { kStart, kCenter, kEnd, kBaseline }; enum class ItemType { kInGridFlow, kOutOfFlow }; // This enum corresponds to each step used to accommodate grid items across // intrinsic tracks according to their min and max track sizing functions, as // defined in https://drafts.csswg.org/css-grid-2/#algo-spanning-items. enum class GridItemContributionType { kForIntrinsicMinimums, kForContentBasedMinimums, kForMaxContentMinimums, kForIntrinsicMaximums, kForMaxContentMaximums, kForFreeSpace }; struct ItemSetIndices { wtf_size_t begin = kNotFound; wtf_size_t end = kNotFound; }; struct CORE_EXPORT GridItemData { explicit GridItemData(const NGBlockNode node) : node(node) {} AutoPlacementType AutoPlacement( GridTrackSizingDirection flow_direction) const; const GridSpan& Span(GridTrackSizingDirection track_direction) const; void SetSpan(const GridSpan& span, GridTrackSizingDirection track_direction); wtf_size_t StartLine(GridTrackSizingDirection track_direction) const; wtf_size_t EndLine(GridTrackSizingDirection track_direction) const; wtf_size_t SpanSize(GridTrackSizingDirection track_direction) const; const TrackSpanProperties& GetTrackSpanProperties( GridTrackSizingDirection track_direction) const; void SetTrackSpanProperty(TrackSpanProperties::PropertyId property, GridTrackSizingDirection track_direction); bool IsSpanningFlexibleTrack( GridTrackSizingDirection track_direction) const; bool IsSpanningIntrinsicTrack( GridTrackSizingDirection track_direction) const; // For this item and track direction, computes and stores the pair of // indices "begin" and "end" such that the item spans every set from the // respective collection's |sets_| with an index in the range [begin, end). // |grid_placement| is used to resolve the grid lines of out of flow items // and it has a default nullptr value for grid items. ItemSetIndices SetIndices( const NGGridLayoutAlgorithmTrackCollection& track_collection, const NGGridPlacement* grid_placement = nullptr); const NGBlockNode node; GridArea resolved_position; NGBoxStrut margins; AxisEdge inline_axis_alignment; AxisEdge block_axis_alignment; ItemType item_type; bool is_inline_axis_stretched; bool is_block_axis_stretched; TrackSpanProperties column_span_properties; TrackSpanProperties row_span_properties; // These fields are used to determine the sets this item spans in the // respective track collection; see |SetIndices|. We use optional since some // scenarios don't require to compute the indices at all. base::Optional column_set_indices; base::Optional row_set_indices; }; struct CORE_EXPORT GridItems { class Iterator : public std::iterator { public: Iterator(Vector* item_data, Vector::const_iterator current_index) : item_data_(item_data), current_index_(current_index) { DCHECK(item_data_); } bool operator!=(const Iterator& other) const { return current_index_ != other.current_index_ || item_data_ != other.item_data_; } Iterator& operator++() { ++current_index_; return *this; } GridItemData* operator->() { DCHECK(current_index_ && *current_index_ < item_data_->size()); return &(item_data_->at(*current_index_)); } GridItemData& operator*() { DCHECK(current_index_ && *current_index_ < item_data_->size()); return item_data_->at(*current_index_); } private: Vector* item_data_; Vector::const_iterator current_index_; }; Iterator begin(); Iterator end(); void Append(const GridItemData& new_item_data); bool IsEmpty() const; // Grid items are appended to |item_data_| in the same order provided by // |NGGridChildIterator|, which iterates over its children in order-modified // document order; we want to keep such order since auto-placement and // painting order rely on it later in the algorithm. Vector item_data; Vector reordered_item_indices; }; // See |SetGeometry|. struct SetOffsetData { SetOffsetData(LayoutUnit offset, wtf_size_t last_indefinite_index) : offset(offset), last_indefinite_index(last_indefinite_index) {} LayoutUnit offset; wtf_size_t last_indefinite_index; }; // Represents the offsets for the sets, and the gutter-size. // // Initially we only know some of the set sizes - others will be indefinite. // To represent this we store both the offset for the set, and the last index // where there was an indefinite set (or kNotFound if everything so far has // been definite). This allows us to get the appropriate size if a grid item // spans only fixed tracks, but will allow us to return an indefinite size if // it spans any indefinite set. // // As an example: // grid-template-rows: auto auto 100px 100px auto 100px; // // Results in: // | auto | auto | 100 | 100 | auto | 100 | // [{0, kNotFound}, {0, 0}, {0, 1}, {100, 1}, {200, 1}, {200, 4}, {300, 4}] // // Various queries (start/end refer to the grid lines): // start: 0, end: 1 -> indefinite as: // "start <= sets[end].last_indefinite_index" // start: 1, end: 3 -> indefinite as: // "start <= sets[end].last_indefinite_index" // start: 2, end: 4 -> 200px // start: 5, end: 6 -> 100px // start: 3, end: 5 -> indefinite as: // "start <= sets[end].last_indefinite_index" struct SetGeometry { Vector sets; LayoutUnit gutter_size; }; // Typically we pass around both the column, and row geometry together. struct GridGeometry { SetGeometry column_geometry; SetGeometry row_geometry; }; explicit NGGridLayoutAlgorithm(const NGLayoutAlgorithmParams& params); scoped_refptr Layout() override; MinMaxSizesResult ComputeMinMaxSizes(const MinMaxSizesInput&) const override; private: friend class NGGridLayoutAlgorithmTest; enum class SizingConstraint { kLayout, kMinContent, kMaxContent }; // Returns the size that a grid item will distribute across the tracks with an // intrinsic sizing function it spans in the relevant track direction. LayoutUnit ContributionSizeForGridItem( const GridGeometry& grid_geometry, const GridItemData& grid_item, GridTrackSizingDirection track_direction, GridItemContributionType contribution_type) const; wtf_size_t ComputeAutomaticRepetitions( GridTrackSizingDirection track_direction) const; void ConstructAndAppendGridItems( GridItems* grid_items, Vector* out_of_flow_items = nullptr) const; GridItemData MeasureGridItem(const NGBlockNode node) const; void BuildBlockTrackCollections( GridItems* grid_items, NGGridBlockTrackCollection* column_track_collection, NGGridBlockTrackCollection* row_track_collection, NGGridPlacement* grid_placement) const; void BuildAlgorithmTrackCollections( GridItems* grid_items, NGGridLayoutAlgorithmTrackCollection* column_track_collection, NGGridLayoutAlgorithmTrackCollection* row_track_collection, NGGridPlacement* grid_placement) const; // Ensure coverage in block collection after grid items have been placed. void EnsureTrackCoverageForGridItems( const GridItems& grid_items, NGGridBlockTrackCollection* track_collection) const; // For every grid item, caches properties of the track sizing functions it // spans (i.e. whether an item spans intrinsic or flexible tracks). void CacheGridItemsTrackSpanProperties( const NGGridLayoutAlgorithmTrackCollection& track_collection, GridItems* grid_items) const; // Initializes the given track collection, and returns the base set geometry. SetGeometry InitializeTrackSizes( NGGridLayoutAlgorithmTrackCollection* track_collection) const; // Calculates from the min and max track sizing functions the used track size. void ComputeUsedTrackSizes( SizingConstraint sizing_constraint, const GridGeometry& grid_geometry, NGGridLayoutAlgorithmTrackCollection* track_collection, GridItems* grid_items) const; // These methods implement the steps of the algorithm for intrinsic track size // resolution defined in https://drafts.csswg.org/css-grid-2/#algo-content. void ResolveIntrinsicTrackSizes( const GridGeometry& grid_geometry, NGGridLayoutAlgorithmTrackCollection* track_collection, GridItems* grid_items) const; void IncreaseTrackSizesToAccommodateGridItems( const GridGeometry& grid_geometry, GridItems::Iterator group_begin, GridItems::Iterator group_end, const bool is_group_spanning_flex_track, GridItemContributionType contribution_type, NGGridLayoutAlgorithmTrackCollection* track_collection) const; void MaximizeTracks( SizingConstraint sizing_constraint, NGGridLayoutAlgorithmTrackCollection* track_collection) const; void StretchAutoTracks( SizingConstraint sizing_constraint, NGGridLayoutAlgorithmTrackCollection* track_collection) const; SetGeometry ComputeSetGeometry( const NGGridLayoutAlgorithmTrackCollection& track_collection, const LayoutUnit available_size) const; // Gets the row or column gap of the grid. LayoutUnit GridGap(GridTrackSizingDirection track_direction, LayoutUnit available_size = kIndefiniteSize) const; LayoutUnit DetermineFreeSpace( SizingConstraint sizing_constraint, const NGGridLayoutAlgorithmTrackCollection& track_collection) const; const NGConstraintSpace CreateConstraintSpace( const GridGeometry& grid_geometry, const GridItemData& grid_item, NGCacheSlot cache_slot, LogicalRect* rect) const; // Layout the |grid_items| based on the offsets provided. void PlaceGridItems(const GridItems& grid_items, const GridGeometry& grid_geometry, LayoutUnit block_size); // Computes the static position, grid area and its offset of out of flow // elements in the grid. void PlaceOutOfFlowItems(const Vector& out_of_flow_items, const GridGeometry& grid_geometry, LayoutUnit block_size); // Gets the out of flow descendants from the container builder and computes // their containing block rect. void PlaceOutOfFlowDescendants( const NGGridLayoutAlgorithmTrackCollection& column_track_collection, const NGGridLayoutAlgorithmTrackCollection& row_track_collection, const GridGeometry& grid_geometry, const NGGridPlacement& grid_placement, LayoutUnit block_size); // Helper method to compute the containing grid area for grid items or the // containing block rect for out of flow elements. LogicalRect ComputeContainingGridAreaRect(const GridGeometry& grid_geometry, const GridItemData& item, LayoutUnit block_size); // Helper method that computes the offset and size of an item. void ComputeOffsetAndSize(const GridItemData& item, const SetGeometry& set_geometry, const GridTrackSizingDirection track_direction, LayoutUnit block_size, LayoutUnit* start_offset, LayoutUnit* size) const; // Determines the position of the out of flow item's container. void DeterminePositionOfOutOfFlowContainer( Vector* out_of_flow_items, const GridTrackSizingDirection track_direction) const; GridTrackSizingDirection AutoFlowDirection() const; LogicalSize border_box_size_; LogicalSize grid_available_size_; LogicalSize grid_min_available_size_; LogicalSize grid_max_available_size_; }; } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_LAYOUT_ALGORITHM_H_