1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "core/style/GridResolvedPosition.h"
8 #include "core/layout/LayoutBox.h"
9 #include "core/style/GridCoordinate.h"
13 static const NamedGridLinesMap
& gridLinesForSide(const ComputedStyle
& style
, GridPositionSide side
)
15 return (side
== ColumnStartSide
|| side
== ColumnEndSide
) ? style
.namedGridColumnLines() : style
.namedGridRowLines();
18 static inline String
implicitNamedGridLineForSide(const String
& lineName
, GridPositionSide side
)
20 return lineName
+ ((side
== ColumnStartSide
|| side
== RowStartSide
) ? "-start" : "-end");
23 static bool isValidNamedLineOrArea(const String
& lineName
, const ComputedStyle
& style
, GridPositionSide side
)
25 const NamedGridLinesMap
& gridLineNames
= gridLinesForSide(style
, side
);
27 return gridLineNames
.contains(implicitNamedGridLineForSide(lineName
, side
)) || gridLineNames
.contains(lineName
);
30 static GridPositionSide
calculateInitialPositionSide(GridTrackSizingDirection direction
)
32 return (direction
== ForColumns
) ? ColumnStartSide
: RowStartSide
;
35 static GridPositionSide
calculateFinalPositionSide(GridTrackSizingDirection direction
)
37 return (direction
== ForColumns
) ? ColumnEndSide
: RowEndSide
;
40 void GridResolvedPosition::initialAndFinalPositionsFromStyle(const ComputedStyle
& gridContainerStyle
, const LayoutBox
& gridItem
, GridTrackSizingDirection direction
, GridPosition
& initialPosition
, GridPosition
& finalPosition
)
42 initialPosition
= (direction
== ForColumns
) ? gridItem
.style()->gridColumnStart() : gridItem
.style()->gridRowStart();
43 finalPosition
= (direction
== ForColumns
) ? gridItem
.style()->gridColumnEnd() : gridItem
.style()->gridRowEnd();
44 GridPositionSide initialPositionSide
= calculateInitialPositionSide(direction
);
45 GridPositionSide finalPositionSide
= calculateFinalPositionSide(direction
);
47 // We must handle the placement error handling code here instead of in the StyleAdjuster because we don't want to
48 // overwrite the specified values.
49 if (initialPosition
.isSpan() && finalPosition
.isSpan())
50 finalPosition
.setAutoPosition();
52 // Try to early detect the case of non existing named grid lines. This way we could assume later that
53 // GridResolvedPosition::resolveGrisPositionFromStyle() always return a valid resolved position.
54 if (initialPosition
.isNamedGridArea() && !isValidNamedLineOrArea(initialPosition
.namedGridLine(), gridContainerStyle
, initialPositionSide
))
55 initialPosition
.setAutoPosition();
57 if (finalPosition
.isNamedGridArea() && !isValidNamedLineOrArea(finalPosition
.namedGridLine(), gridContainerStyle
, finalPositionSide
))
58 finalPosition
.setAutoPosition();
60 // If the grid item has an automatic position and a grid span for a named line in a given dimension, instead treat the grid span as one.
61 if (initialPosition
.isAuto() && finalPosition
.isSpan() && !finalPosition
.namedGridLine().isNull())
62 finalPosition
.setSpanPosition(1, String());
63 if (finalPosition
.isAuto() && initialPosition
.isSpan() && !initialPosition
.namedGridLine().isNull())
64 initialPosition
.setSpanPosition(1, String());
67 GridSpan
GridResolvedPosition::resolveGridPositionsFromAutoPlacementPosition(const ComputedStyle
& gridContainerStyle
, const LayoutBox
& gridItem
, GridTrackSizingDirection direction
, const GridResolvedPosition
& resolvedInitialPosition
)
69 GridPosition initialPosition
, finalPosition
;
70 initialAndFinalPositionsFromStyle(gridContainerStyle
, gridItem
, direction
, initialPosition
, finalPosition
);
72 GridPositionSide finalPositionSide
= calculateFinalPositionSide(direction
);
74 // This method will only be used when both positions need to be resolved against the opposite one.
75 ASSERT(initialPosition
.shouldBeResolvedAgainstOppositePosition() && finalPosition
.shouldBeResolvedAgainstOppositePosition());
77 GridResolvedPosition resolvedFinalPosition
= resolvedInitialPosition
;
79 if (initialPosition
.isSpan())
80 return *resolveGridPositionAgainstOppositePosition(gridContainerStyle
, resolvedInitialPosition
, initialPosition
, finalPositionSide
);
81 if (finalPosition
.isSpan())
82 return *resolveGridPositionAgainstOppositePosition(gridContainerStyle
, resolvedInitialPosition
, finalPosition
, finalPositionSide
);
84 return GridSpan(resolvedInitialPosition
, resolvedFinalPosition
);
87 PassOwnPtr
<GridSpan
> GridResolvedPosition::resolveGridPositionsFromStyle(const ComputedStyle
& gridContainerStyle
, const LayoutBox
& gridItem
, GridTrackSizingDirection direction
)
89 GridPosition initialPosition
, finalPosition
;
90 initialAndFinalPositionsFromStyle(gridContainerStyle
, gridItem
, direction
, initialPosition
, finalPosition
);
92 GridPositionSide initialPositionSide
= calculateInitialPositionSide(direction
);
93 GridPositionSide finalPositionSide
= calculateFinalPositionSide(direction
);
95 if (initialPosition
.shouldBeResolvedAgainstOppositePosition() && finalPosition
.shouldBeResolvedAgainstOppositePosition()) {
96 // We can't get our grid positions without running the auto placement algorithm.
100 if (initialPosition
.shouldBeResolvedAgainstOppositePosition()) {
101 // Infer the position from the final position ('auto / 1' or 'span 2 / 3' case).
102 GridResolvedPosition finalResolvedPosition
= resolveGridPositionFromStyle(gridContainerStyle
, finalPosition
, finalPositionSide
);
103 return resolveGridPositionAgainstOppositePosition(gridContainerStyle
, finalResolvedPosition
, initialPosition
, initialPositionSide
);
106 if (finalPosition
.shouldBeResolvedAgainstOppositePosition()) {
107 // Infer our position from the initial position ('1 / auto' or '3 / span 2' case).
108 GridResolvedPosition initialResolvedPosition
= resolveGridPositionFromStyle(gridContainerStyle
, initialPosition
, initialPositionSide
);
109 return resolveGridPositionAgainstOppositePosition(gridContainerStyle
, initialResolvedPosition
, finalPosition
, finalPositionSide
);
112 GridResolvedPosition resolvedInitialPosition
= resolveGridPositionFromStyle(gridContainerStyle
, initialPosition
, initialPositionSide
);
113 GridResolvedPosition resolvedFinalPosition
= resolveGridPositionFromStyle(gridContainerStyle
, finalPosition
, finalPositionSide
);
115 // If 'grid-after' specifies a line at or before that specified by 'grid-before', it computes to 'span 1'.
116 if (resolvedFinalPosition
< resolvedInitialPosition
)
117 resolvedFinalPosition
= resolvedInitialPosition
;
119 return adoptPtr(new GridSpan(resolvedInitialPosition
, resolvedFinalPosition
));
122 size_t GridResolvedPosition::explicitGridColumnCount(const ComputedStyle
& gridContainerStyle
)
124 return std::min(gridContainerStyle
.gridTemplateColumns().size(), kGridMaxTracks
);
127 size_t GridResolvedPosition::explicitGridRowCount(const ComputedStyle
& gridContainerStyle
)
129 return std::min(gridContainerStyle
.gridTemplateRows().size(), kGridMaxTracks
);
132 size_t GridResolvedPosition::explicitGridSizeForSide(const ComputedStyle
& gridContainerStyle
, GridPositionSide side
)
134 return (side
== ColumnStartSide
|| side
== ColumnEndSide
) ? explicitGridColumnCount(gridContainerStyle
) : explicitGridRowCount(gridContainerStyle
);
137 GridResolvedPosition
GridResolvedPosition::resolveNamedGridLinePositionFromStyle(const ComputedStyle
& gridContainerStyle
, const GridPosition
& position
, GridPositionSide side
)
139 ASSERT(!position
.namedGridLine().isNull());
141 const NamedGridLinesMap
& gridLinesNames
= gridLinesForSide(gridContainerStyle
, side
);
142 NamedGridLinesMap::const_iterator it
= gridLinesNames
.find(position
.namedGridLine());
143 if (it
== gridLinesNames
.end()) {
144 if (position
.isPositive())
145 return GridResolvedPosition(0);
146 const size_t lastLine
= explicitGridSizeForSide(gridContainerStyle
, side
);
147 return adjustGridPositionForSide(lastLine
, side
);
150 size_t namedGridLineIndex
;
151 if (position
.isPositive())
152 namedGridLineIndex
= std::min
<size_t>(position
.integerPosition(), it
->value
.size()) - 1;
154 namedGridLineIndex
= std::max
<int>(it
->value
.size() - abs(position
.integerPosition()), 0);
155 return adjustGridPositionForSide(it
->value
[namedGridLineIndex
], side
);
158 GridResolvedPosition
GridResolvedPosition::resolveGridPositionFromStyle(const ComputedStyle
& gridContainerStyle
, const GridPosition
& position
, GridPositionSide side
)
160 switch (position
.type()) {
161 case ExplicitPosition
: {
162 ASSERT(position
.integerPosition());
164 if (!position
.namedGridLine().isNull())
165 return resolveNamedGridLinePositionFromStyle(gridContainerStyle
, position
, side
);
167 // Handle <integer> explicit position.
168 if (position
.isPositive())
169 return adjustGridPositionForSide(position
.integerPosition() - 1, side
);
171 size_t resolvedPosition
= abs(position
.integerPosition()) - 1;
172 const size_t endOfTrack
= explicitGridSizeForSide(gridContainerStyle
, side
);
174 // Per http://lists.w3.org/Archives/Public/www-style/2013Mar/0589.html, we clamp negative value to the first line.
175 if (endOfTrack
< resolvedPosition
)
176 return GridResolvedPosition(0);
178 return adjustGridPositionForSide(endOfTrack
- resolvedPosition
, side
);
180 case NamedGridAreaPosition
:
182 // First attempt to match the grid area's edge to a named grid area: if there is a named line with the name
183 // ''<custom-ident>-start (for grid-*-start) / <custom-ident>-end'' (for grid-*-end), contributes the first such
184 // line to the grid item's placement.
185 String namedGridLine
= position
.namedGridLine();
186 ASSERT(isValidNamedLineOrArea(namedGridLine
, gridContainerStyle
, side
));
188 const NamedGridLinesMap
& gridLineNames
= gridLinesForSide(gridContainerStyle
, side
);
189 NamedGridLinesMap::const_iterator implicitLineIter
= gridLineNames
.find(implicitNamedGridLineForSide(namedGridLine
, side
));
190 if (implicitLineIter
!= gridLineNames
.end())
191 return adjustGridPositionForSide(implicitLineIter
->value
[0], side
);
193 // Otherwise, if there is a named line with the specified name, contributes the first such line to the grid
195 NamedGridLinesMap::const_iterator explicitLineIter
= gridLineNames
.find(namedGridLine
);
196 if (explicitLineIter
!= gridLineNames
.end())
197 return adjustGridPositionForSide(explicitLineIter
->value
[0], side
);
199 // If none of the above works specs mandate us to treat it as auto BUT we should have detected it before calling
200 // this function in GridResolvedPosition::resolveGridPositionsFromStyle(). We should be also covered by the
201 // ASSERT at the beginning of this block.
202 ASSERT_NOT_REACHED();
203 return GridResolvedPosition(0);
207 // 'auto' and span depend on the opposite position for resolution (e.g. grid-row: auto / 1 or grid-column: span 3 / "myHeader").
208 ASSERT_NOT_REACHED();
209 return GridResolvedPosition(0);
211 ASSERT_NOT_REACHED();
212 return GridResolvedPosition(0);
215 PassOwnPtr
<GridSpan
> GridResolvedPosition::resolveGridPositionAgainstOppositePosition(const ComputedStyle
& gridContainerStyle
, const GridResolvedPosition
& resolvedOppositePosition
, const GridPosition
& position
, GridPositionSide side
)
217 if (position
.isAuto())
218 return GridSpan::create(resolvedOppositePosition
, resolvedOppositePosition
);
220 ASSERT(position
.isSpan());
221 ASSERT(position
.spanPosition() > 0);
223 if (!position
.namedGridLine().isNull()) {
224 // span 2 'c' -> we need to find the appropriate grid line before / after our opposite position.
225 return resolveNamedGridLinePositionAgainstOppositePosition(gridContainerStyle
, resolvedOppositePosition
, position
, side
);
228 return GridSpan::createWithSpanAgainstOpposite(resolvedOppositePosition
, position
, side
);
231 PassOwnPtr
<GridSpan
> GridResolvedPosition::resolveNamedGridLinePositionAgainstOppositePosition(const ComputedStyle
& gridContainerStyle
, const GridResolvedPosition
& resolvedOppositePosition
, const GridPosition
& position
, GridPositionSide side
)
233 ASSERT(position
.isSpan());
234 ASSERT(!position
.namedGridLine().isNull());
235 // Negative positions are not allowed per the specification and should have been handled during parsing.
236 ASSERT(position
.spanPosition() > 0);
238 const NamedGridLinesMap
& gridLinesNames
= gridLinesForSide(gridContainerStyle
, side
);
239 NamedGridLinesMap::const_iterator it
= gridLinesNames
.find(position
.namedGridLine());
241 // If there is no named grid line of that name, we resolve the position to 'auto' (which is equivalent to 'span 1' in this case).
242 // See http://lists.w3.org/Archives/Public/www-style/2013Jun/0394.html.
243 if (it
== gridLinesNames
.end())
244 return GridSpan::create(resolvedOppositePosition
, resolvedOppositePosition
);
246 return GridSpan::createWithNamedSpanAgainstOpposite(resolvedOppositePosition
, position
, side
, it
->value
);