blob: 06db259b6edd923fe0c622fe2ac4d8e2243746b6 [file]
/*
* Copyright (C) 2011, 2022 Apple Inc. All rights reserved.
* Copyright (C) 2013-2017 Igalia S.L.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "Grid.h"
#include "GridTrackSizingAlgorithm.h"
#include "RenderBlock.h"
#include "StyleGridTrackSizingDirection.h"
#include <wtf/TZoneMalloc.h>
namespace WTF {
template<typename T>
class Range;
}
namespace WebCore {
namespace LayoutIntegration {
class GridLayout;
}
class GridArea;
class RenderGridLayoutState;
class GridLanesLayout;
class GridLanesResult;
class GridSpan;
class LayoutRange;
// The result of a grid lanes placement only exists for the duration of a single layout (or intrinsic
// width computation) of a grid lanes container, so it is threaded through the layout call chain as a
// non-owning reference rather than living on the renderer. It is std::nullopt for grids which are
// not performing a grid lanes layout.
using GridLanesResultRef = std::optional<std::reference_wrapper<const GridLanesResult>>;
struct ContentAlignmentData {
LayoutUnit positionOffset;
LayoutUnit distributionOffset;
};
enum class GridAxisPosition : uint8_t { GridAxisStart, GridAxisEnd, GridAxisCenter };
enum class SubgridDidChange : bool { No, Yes };
class GridItemSizeCache {
public:
void setSizeForGridItem(const RenderBox& gridItem, LayoutUnit size);
std::optional<LayoutUnit> NODELETE sizeForItem(const RenderBox& gridItem) const;
void invalidateSizeForItem(const RenderBox& gridItem);
private:
SingleThreadWeakHashMap<const RenderBox, std::optional<LayoutUnit>> m_sizes;
};
class RenderGrid final : public RenderBlock {
WTF_MAKE_TZONE_ALLOCATED(RenderGrid);
WTF_OVERRIDE_DELETE_FOR_CHECKED_PTR(RenderGrid);
public:
RenderGrid(Element&, Style::ComputedStyle&&);
virtual ~RenderGrid();
Element& element() const { return downcast<Element>(nodeForNonAnonymous()); }
void styleDidChange(Style::Difference, const Style::ComputedStyle* oldStyle) override;
void layoutBlock(RelayoutChildren, LayoutUnit pageLogicalHeight = 0_lu) override;
bool canDropAnonymousBlockChild() const override { return false; }
bool isComputingTrackSizes() const { return m_isComputingTrackSizes; }
bool hasDefiniteLogicalHeight() const;
const std::optional<LayoutUnit> availableLogicalHeightForContentBox() const;
void setNeedsItemPlacement(SubgridDidChange descendantSubgridsNeedItemPlacement = SubgridDidChange::No);
const Vector<LayoutUnit>& trackSizesForComputedStyle(Style::GridTrackSizingDirection) const LIFETIME_BOUND;
const Vector<LayoutUnit>& columnPositions() const LIFETIME_BOUND { return m_columnPositions; }
const Vector<LayoutUnit>& rowPositions() const LIFETIME_BOUND { return m_rowPositions; }
const Vector<LayoutUnit>& positions(Style::GridTrackSizingDirection direction) const LIFETIME_BOUND { return direction == Style::GridTrackSizingDirection::Columns ? columnPositions() : rowPositions(); }
unsigned autoRepeatCountForDirection(Style::GridTrackSizingDirection direction) const { return currentGrid().autoRepeatTracks(direction); }
unsigned explicitGridStartForDirection(Style::GridTrackSizingDirection direction) const { return currentGrid().explicitGridStart(direction); }
// Required by GridTrackSizingAlgorithm. Keep them under control.
LayoutUnit guttersSize(Style::GridTrackSizingDirection, unsigned startLine, unsigned span, std::optional<LayoutUnit> availableSize) const;
LayoutUnit NODELETE gridItemOffset(Style::GridTrackSizingDirection) const;
std::optional<LayoutUnit> explicitIntrinsicInnerLogicalSize(Style::GridTrackSizingDirection) const;
void updateGridAreaLogicalSize(RenderBox&, std::optional<LayoutUnit> width, std::optional<LayoutUnit> height) const;
bool isBaselineAlignmentForGridItem(const RenderBox&) const;
bool isBaselineAlignmentForGridItem(const RenderBox& gridItem, Style::GridTrackSizingDirection alignmentContext) const;
StyleContentAlignmentData contentAlignment(Style::GridTrackSizingDirection) const;
StyleSelfAlignmentData selfAlignmentForGridItem(const RenderBox&, LogicalBoxAxis containingAxis, StretchingMode = StretchingMode::Normal, const Style::ComputedStyle* = nullptr) const;
bool willStretchItem(const RenderBox& item, LogicalBoxAxis containingAxis, StretchingMode = StretchingMode::Normal) const override;
// These functions handle the actual implementation of layoutBlock based on if
// the grid is a standard grid or a grid lanes one. While grid lanes layout is an extension of grid,
// keeping the logic in the same function was leading to a messy amount of if statements being added to handle
// specific grid lanes cases.
void layoutGrid(RelayoutChildren);
void layoutGridLanes(RelayoutChildren);
// Computes the span relative to this RenderGrid, even if the RenderBox is a grid item
// of a descendant subgrid.
GridSpan gridSpanForGridItem(const RenderBox&, Style::GridTrackSizingDirection) const;
bool isSubgrid() const;
bool isSubgrid(Style::GridTrackSizingDirection) const;
bool isSubgridRows() const { return isSubgrid(Style::GridTrackSizingDirection::Rows); }
bool isSubgridColumns() const { return isSubgrid(Style::GridTrackSizingDirection::Columns); }
bool isSubgridInParentDirection(Style::GridTrackSizingDirection parentDirection) const;
// Returns true if this grid is inheriting subgridded tracks for
// the given direction from the specified ancestor. This handles
// nested subgrids, where ancestor may not be our direct parent.
bool isSubgridOf(Style::GridTrackSizingDirection, const RenderGrid& ancestor) const;
bool NODELETE isGridLanes() const;
bool isStackingAxis(Style::GridTrackSizingDirection) const;
bool isStackingAxis(LogicalBoxAxis axis) const { return isStackingAxis(Style::gridTrackSizingDirection(axis)); }
bool hasStackingAxisRows() const { return isStackingAxis(Style::GridTrackSizingDirection::Rows); }
bool hasStackingAxisColumns() const { return isStackingAxis(Style::GridTrackSizingDirection::Columns); }
const Grid& NODELETE currentGrid() const LIFETIME_BOUND;
Grid& NODELETE currentGrid() LIFETIME_BOUND;
unsigned numTracks(Style::GridTrackSizingDirection) const;
void placeItems();
std::optional<LayoutUnit> availableSpaceForGutters(Style::GridTrackSizingDirection) const;
LayoutUnit gridGap(Style::GridTrackSizingDirection) const;
LayoutUnit gridGap(Style::GridTrackSizingDirection, std::optional<LayoutUnit> availableSize) const;
// The stacking axis content size produced by the most recent grid lanes layout. Unlike the rest of
// the GridLanesResult this has to outlive layout, because the web inspector's grid overlay reads it
// at paint time. Nothing in layout should use this; layout threads the result itself through as a
// GridLanesResultRef instead.
LayoutUnit NODELETE gridLanesContentSizeForWebInspectorOverlay() const { return m_gridLanesContentSizeForWebInspectorOverlay; }
void updateIntrinsicLogicalHeightsForRowSizingFirstPassCacheAvailability();
std::optional<GridItemSizeCache>& NODELETE intrinsicLogicalHeightsForRowSizingFirstPass() const LIFETIME_BOUND;
bool shouldCheckExplicitIntrinsicInnerLogicalSize(Style::GridTrackSizingDirection) const;
// Checks both the grid container and the grid since the grid container is sized
// according to the rules of the formatting context it lives in while the size of the
// grid is determined by the lines/grid areas which come from track sizing.
bool isExtrinsicallySized() const;
private:
friend class GridTrackSizingAlgorithm;
friend class GridTrackSizingAlgorithmStrategy;
friend class GridLanesLayout;
friend class PositionedLayoutConstraints;
friend class LayoutIntegration::GridLayout;
inline void updateGridAreaWithEstimate(RenderBox& gridItem, const GridTrackSizingAlgorithm&) const;
inline void updateGridAreaIncludingAlignment(RenderBox& gridItem) const;
void computeLayoutRequirementsForItemsBeforeLayout(RenderGridLayoutState&) const;
bool canSetColumnAxisStretchRequirementForItem(const RenderBox&) const;
ASCIILiteral renderName() const override;
std::pair<LayoutUnit, LayoutUnit> computeIntrinsicLogicalWidths() const override;
bool selfAlignmentChangedToStretch(LogicalBoxAxis containingAxis, const Style::ComputedStyle& oldStyle, const Style::ComputedStyle& newStyle, const RenderBox& gridItem) const;
bool selfAlignmentChangedFromStretch(LogicalBoxAxis containingAxis, const Style::ComputedStyle& oldStyle, const Style::ComputedStyle& newStyle, const RenderBox& gridItem) const;
SubgridDidChange NODELETE subgridDidChange(const Style::ComputedStyle& oldStyle) const;
bool NODELETE explicitGridDidResize(const Style::ComputedStyle&) const;
bool namedGridLinesDefinitionDidChange(const Style::ComputedStyle&) const;
bool implicitGridLinesDefinitionDidChange(const Style::ComputedStyle&) const;
unsigned computeAutoRepeatTracksCount(Style::GridTrackSizingDirection, std::optional<LayoutUnit> availableSize) const;
unsigned clampAutoRepeatTracks(Style::GridTrackSizingDirection, unsigned autoRepeatTracks) const;
WTF::Range<size_t> NODELETE autoRepeatTracksRange(Style::GridTrackSizingDirection) const;
std::unique_ptr<OrderedTrackIndexSet> computeEmptyTracksForAutoRepeat(Style::GridTrackSizingDirection) const;
enum class ShouldUpdateGridAreaLogicalSize : bool { No, Yes };
void performPreLayoutForGridItems(const GridTrackSizingAlgorithm&, const ShouldUpdateGridAreaLogicalSize) const;
void placeItemsOnGrid(std::optional<LayoutUnit> availableLogicalWidth);
void populateExplicitGridAndOrderIterator();
GridArea createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(const RenderBox&, Style::GridTrackSizingDirection, const GridSpan&) const;
bool isPlacedWithinExtrinsicallySizedExplicitTracks(const RenderBox&) const;
void placeSpecifiedMajorAxisItemsOnGrid(const Vector<RenderBox*>&);
void placeAutoMajorAxisItemsOnGrid(const Vector<RenderBox*>&);
typedef std::pair<unsigned, unsigned> AutoPlacementCursor;
void placeAutoMajorAxisItemOnGrid(RenderBox&, AutoPlacementCursor&);
Style::GridTrackSizingDirection NODELETE autoPlacementMajorAxisDirection() const;
Style::GridTrackSizingDirection NODELETE autoPlacementMinorAxisDirection() const;
static bool itemGridAreaIsWithinImplicitGrid(const GridArea& area, unsigned gridAxisLinesCount, Style::GridTrackSizingDirection gridAxisDirection)
{
auto itemSpan = area.span(gridAxisDirection);
return itemSpan.startLine() < gridAxisLinesCount && itemSpan.endLine() < gridAxisLinesCount;
}
bool canPerformSimplifiedLayout() const final;
void prepareGridItemForPositionedLayout(RenderBox&);
bool NODELETE hasStaticPositionForGridItem(const RenderBox&, Style::GridTrackSizingDirection) const;
void layoutOutOfFlowBox(RenderBox&, RelayoutChildren, bool fixedPositionObjectsOnly) override;
void computeTrackSizesForDefiniteSize(Style::GridTrackSizingDirection, LayoutUnit availableSpace, RenderGridLayoutState&);
std::pair<LayoutUnit, LayoutUnit> computeTrackSizesForIndefiniteSize(GridTrackSizingAlgorithm&, Style::GridTrackSizingDirection, RenderGridLayoutState&) const;
LayoutUnit computeTrackBasedLogicalHeight() const;
void repeatTracksSizingIfNeeded(LayoutUnit availableSpaceForColumns, LayoutUnit availableSpaceForRows, RenderGridLayoutState&);
void updateGridAreaForAspectRatioItems(const Vector<RenderBox*>&, RenderGridLayoutState&, GridLanesResultRef);
void layoutGridItems(RenderGridLayoutState&, GridLanesResultRef);
void layoutGridLanesItems(RenderGridLayoutState&, const GridLanesResult&);
void populateGridPositionsForDirection(const GridTrackSizingAlgorithm&, Style::GridTrackSizingDirection, GridLanesResultRef);
LayoutRange gridAreaRangeForOutOfFlow(const RenderBox&, Style::GridTrackSizingDirection) const;
std::pair<LayoutUnit, LayoutUnit> gridAreaPositionForInFlowGridItem(const RenderBox&, Style::GridTrackSizingDirection) const;
GridAxisPosition columnAxisPositionForGridItem(const RenderBox&) const;
GridAxisPosition rowAxisPositionForGridItem(const RenderBox&) const;
LayoutUnit columnAxisOffsetForGridItem(const RenderBox&, GridLanesResultRef) const;
LayoutUnit rowAxisOffsetForGridItem(const RenderBox&, GridLanesResultRef) const;
ContentAlignmentData computeContentPositionAndDistributionOffset(Style::GridTrackSizingDirection, const LayoutUnit& availableFreeSpace, unsigned numberOfGridTracks) const;
void setLogicalPositionForGridItem(RenderBox&, GridLanesResultRef) const;
LayoutUnit logicalOffsetForGridItem(const RenderBox&, Style::GridTrackSizingDirection, GridLanesResultRef) const;
LayoutUnit gridAreaBreadthForGridItemIncludingAlignmentOffsets(const RenderBox&, Style::GridTrackSizingDirection) const;
void paintChildren(PaintInfo& forSelf, const LayoutPoint& paintOffset, PaintInfo& forChild, bool usePrintRect) override;
bool hitTestChildren(const HitTestRequest&, HitTestResult&, const HitTestLocation&, const LayoutPoint& adjustedLocation, HitTestAction) override;
LayoutOptionalOutsets allowedLayoutOverflow() const override;
LayoutRect contentOverflowRect() const;
void applyStretchAlignmentToGridItemIfNeeded(RenderBox&, RenderGridLayoutState&);
void applySubgridStretchAlignmentToGridItemIfNeeded(RenderBox&);
std::optional<LayoutUnit> firstLineBaseline() const final;
std::optional<LayoutUnit> lastLineBaseline() const final;
const RenderBox* baselineGridItem(ItemPosition alignment) const;
LayoutUnit columnAxisBaselineOffsetForGridItem(const RenderBox&) const;
LayoutUnit rowAxisBaselineOffsetForGridItem(const RenderBox&) const;
unsigned NODELETE nonCollapsedTracks(Style::GridTrackSizingDirection) const;
LayoutUnit translateRTLCoordinate(LayoutUnit) const;
bool shouldResetLogicalHeightBeforeLayout() const override { return true; }
bool aspectRatioPrefersInline(const RenderBox& gridItem, bool blockFlowIsColumnAxis);
Vector<RenderBox*> computeAspectRatioDependentAndBaselineItems(RenderGridLayoutState&);
GridSpan gridSpanForOutOfFlowGridItem(const RenderBox&, Style::GridTrackSizingDirection) const;
bool computeGridPositionsForOutOfFlowGridItem(const RenderBox&, Style::GridTrackSizingDirection, int&, bool&, int&, bool&) const;
AutoRepeatType NODELETE autoRepeatType(Style::GridTrackSizingDirection) const;
Vector<LayoutUnit>& positions(Style::GridTrackSizingDirection direction) LIFETIME_BOUND { return direction == Style::GridTrackSizingDirection::Columns ? m_columnPositions : m_rowPositions; }
// https://drafts.csswg.org/css-grid-2/#resolved-track-list
// The resolved value of grid-template-columns / grid-template-rows for a grid container is its
// used value: every track listed individually with its size serialized as a length in pixels.
// We compute this once at the end of layout instead of on demand for each getComputedStyle query.
Vector<LayoutUnit> computeResolvedTrackList(Style::GridTrackSizingDirection) const;
void updateResolvedTrackListsAfterLayout();
void setResolvedTrackSizes(Vector<LayoutUnit>&& columnSizes, Vector<LayoutUnit>&& rowSizes);
ContentAlignmentData& offsetBetweenTracks(Style::GridTrackSizingDirection direction) LIFETIME_BOUND { return direction == Style::GridTrackSizingDirection::Columns ? m_offsetBetweenColumns : m_offsetBetweenRows; }
const ContentAlignmentData& offsetBetweenTracks(Style::GridTrackSizingDirection direction) const LIFETIME_BOUND { return direction == Style::GridTrackSizingDirection::Columns ? m_offsetBetweenColumns : m_offsetBetweenRows; }
bool canCreateIntrinsicLogicalHeightsForRowSizingFirstPassCache() const;
class GridWrapper {
Grid m_layoutGrid;
public:
GridWrapper(RenderGrid&);
void NODELETE resetCurrentGrid() const;
mutable std::reference_wrapper<Grid> m_currentGrid { std::ref(m_layoutGrid) };
} m_grid;
// FIXME: Refactor m_trackSizingAlgorithm to be inside of layoutGrid and layoutGridLanes.
// https://bugs.webkit.org/show_bug.cgi?id=277496
GridTrackSizingAlgorithm m_trackSizingAlgorithm;
Vector<LayoutUnit> m_columnPositions;
Vector<LayoutUnit> m_rowPositions;
ContentAlignmentData m_offsetBetweenColumns;
ContentAlignmentData m_offsetBetweenRows;
LayoutUnit m_gridLanesContentSizeForWebInspectorOverlay;
// Cached resolved value of grid-template-columns / grid-template-rows (see computeResolvedTrackList).
// Invalidated when a full (non-simplified) layout begins and repopulated at the end of that layout,
// so a stale read trips the assert in trackSizesForComputedStyle().
struct ResolvedTrackList {
Vector<LayoutUnit> columnSizes;
Vector<LayoutUnit> rowSizes;
bool isValid { false };
void invalidate()
{
isValid = false;
columnSizes.clear();
rowSizes.clear();
}
const Vector<LayoutUnit>& sizes(Style::GridTrackSizingDirection direction) const LIFETIME_BOUND { return direction == Style::GridTrackSizingDirection::Columns ? columnSizes : rowSizes; }
Vector<LayoutUnit>& sizes(Style::GridTrackSizingDirection direction) LIFETIME_BOUND { return direction == Style::GridTrackSizingDirection::Columns ? columnSizes : rowSizes; }
} m_resolvedTrackList;
mutable std::optional<GridItemSizeCache> m_intrinsicLogicalHeightsForRowSizingFirstPass;
bool layoutUsingGridFormattingContext();
std::optional<bool> m_hasGridFormattingContextLayout;
mutable bool m_isComputingTrackSizes { false };
};
} // namespace WebCore
SPECIALIZE_TYPE_TRAITS_RENDER_OBJECT(RenderGrid, isRenderGrid())