• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "config.h"
6 #include "core/rendering/style/GridResolvedPosition.h"
7 
8 #include "core/rendering/RenderBox.h"
9 #include "core/rendering/style/GridCoordinate.h"
10 
11 namespace WebCore {
12 
gridLinesForSide(const RenderStyle & style,GridPositionSide side)13 static const NamedGridLinesMap& gridLinesForSide(const RenderStyle& style, GridPositionSide side)
14 {
15     return (side == ColumnStartSide || side == ColumnEndSide) ? style.namedGridColumnLines() : style.namedGridRowLines();
16 }
17 
implicitNamedGridLineForSide(const String & lineName,GridPositionSide side)18 static inline String implicitNamedGridLineForSide(const String& lineName, GridPositionSide side)
19 {
20     return lineName + ((side == ColumnStartSide || side == RowStartSide) ? "-start" : "-end");
21 }
22 
isValidNamedLineOrArea(const String & lineName,const RenderStyle & style,GridPositionSide side)23 static bool isValidNamedLineOrArea(const String& lineName, const RenderStyle& style, GridPositionSide side)
24 {
25     const NamedGridLinesMap& gridLineNames = gridLinesForSide(style, side);
26 
27     return gridLineNames.contains(implicitNamedGridLineForSide(lineName, side)) || gridLineNames.contains(lineName);
28 }
29 
calculateInitialPositionSide(GridTrackSizingDirection direction)30 static GridPositionSide calculateInitialPositionSide(GridTrackSizingDirection direction)
31 {
32     return (direction == ForColumns) ? ColumnStartSide : RowStartSide;
33 }
34 
calculateFinalPositionSide(GridTrackSizingDirection direction)35 static GridPositionSide calculateFinalPositionSide(GridTrackSizingDirection direction)
36 {
37     return (direction == ForColumns) ? ColumnEndSide : RowEndSide;
38 }
39 
initialAndFinalPositionsFromStyle(const RenderStyle & gridContainerStyle,const RenderBox & gridItem,GridTrackSizingDirection direction,GridPosition & initialPosition,GridPosition & finalPosition)40 void GridResolvedPosition::initialAndFinalPositionsFromStyle(const RenderStyle& gridContainerStyle, const RenderBox& gridItem, GridTrackSizingDirection direction, GridPosition& initialPosition, GridPosition& finalPosition)
41 {
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);
46 
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();
51 
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();
56 
57     if (finalPosition.isNamedGridArea() && !isValidNamedLineOrArea(finalPosition.namedGridLine(), gridContainerStyle, finalPositionSide))
58         finalPosition.setAutoPosition();
59 
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());
65 }
66 
resolveGridPositionsFromAutoPlacementPosition(const RenderStyle & gridContainerStyle,const RenderBox & gridItem,GridTrackSizingDirection direction,const GridResolvedPosition & resolvedInitialPosition)67 GridSpan GridResolvedPosition::resolveGridPositionsFromAutoPlacementPosition(const RenderStyle& gridContainerStyle, const RenderBox& gridItem, GridTrackSizingDirection direction, const GridResolvedPosition& resolvedInitialPosition)
68 {
69     GridPosition initialPosition, finalPosition;
70     initialAndFinalPositionsFromStyle(gridContainerStyle, gridItem, direction, initialPosition, finalPosition);
71 
72     GridPositionSide finalPositionSide = calculateFinalPositionSide(direction);
73 
74     // This method will only be used when both positions need to be resolved against the opposite one.
75     ASSERT(initialPosition.shouldBeResolvedAgainstOppositePosition() && finalPosition.shouldBeResolvedAgainstOppositePosition());
76 
77     GridResolvedPosition resolvedFinalPosition = resolvedInitialPosition;
78 
79     if (initialPosition.isSpan()) {
80         resolvedFinalPosition = resolveGridPositionAgainstOppositePosition(gridContainerStyle, resolvedInitialPosition, initialPosition, finalPositionSide)->resolvedFinalPosition;
81     } else if (finalPosition.isSpan()) {
82         resolvedFinalPosition = resolveGridPositionAgainstOppositePosition(gridContainerStyle, resolvedInitialPosition, finalPosition, finalPositionSide)->resolvedFinalPosition;
83     }
84 
85     return GridSpan(resolvedInitialPosition, resolvedFinalPosition);
86 }
87 
resolveGridPositionsFromStyle(const RenderStyle & gridContainerStyle,const RenderBox & gridItem,GridTrackSizingDirection direction)88 PassOwnPtr<GridSpan> GridResolvedPosition::resolveGridPositionsFromStyle(const RenderStyle& gridContainerStyle, const RenderBox& gridItem, GridTrackSizingDirection direction)
89 {
90     GridPosition initialPosition, finalPosition;
91     initialAndFinalPositionsFromStyle(gridContainerStyle, gridItem, direction, initialPosition, finalPosition);
92 
93     GridPositionSide initialPositionSide = calculateInitialPositionSide(direction);
94     GridPositionSide finalPositionSide = calculateFinalPositionSide(direction);
95 
96     if (initialPosition.shouldBeResolvedAgainstOppositePosition() && finalPosition.shouldBeResolvedAgainstOppositePosition()) {
97         if (gridContainerStyle.gridAutoFlow() == AutoFlowNone)
98             return adoptPtr(new GridSpan(0, 0));
99 
100         // We can't get our grid positions without running the auto placement algorithm.
101         return nullptr;
102     }
103 
104     if (initialPosition.shouldBeResolvedAgainstOppositePosition()) {
105         // Infer the position from the final position ('auto / 1' or 'span 2 / 3' case).
106         GridResolvedPosition finalResolvedPosition = resolveGridPositionFromStyle(gridContainerStyle, finalPosition, finalPositionSide);
107         return resolveGridPositionAgainstOppositePosition(gridContainerStyle, finalResolvedPosition, initialPosition, initialPositionSide);
108     }
109 
110     if (finalPosition.shouldBeResolvedAgainstOppositePosition()) {
111         // Infer our position from the initial position ('1 / auto' or '3 / span 2' case).
112         GridResolvedPosition initialResolvedPosition = resolveGridPositionFromStyle(gridContainerStyle, initialPosition, initialPositionSide);
113         return resolveGridPositionAgainstOppositePosition(gridContainerStyle, initialResolvedPosition, finalPosition, finalPositionSide);
114     }
115 
116     GridResolvedPosition resolvedInitialPosition = resolveGridPositionFromStyle(gridContainerStyle, initialPosition, initialPositionSide);
117     GridResolvedPosition resolvedFinalPosition = resolveGridPositionFromStyle(gridContainerStyle, finalPosition, finalPositionSide);
118 
119     // If 'grid-after' specifies a line at or before that specified by 'grid-before', it computes to 'span 1'.
120     if (resolvedFinalPosition < resolvedInitialPosition)
121         resolvedFinalPosition = resolvedInitialPosition;
122 
123     return adoptPtr(new GridSpan(resolvedInitialPosition, resolvedFinalPosition));
124 }
125 
explicitGridColumnCount(const RenderStyle & gridContainerStyle)126 size_t GridResolvedPosition::explicitGridColumnCount(const RenderStyle& gridContainerStyle)
127 {
128     return gridContainerStyle.gridTemplateColumns().size();
129 }
130 
explicitGridRowCount(const RenderStyle & gridContainerStyle)131 size_t GridResolvedPosition::explicitGridRowCount(const RenderStyle& gridContainerStyle)
132 {
133     return gridContainerStyle.gridTemplateRows().size();
134 }
135 
explicitGridSizeForSide(const RenderStyle & gridContainerStyle,GridPositionSide side)136 size_t GridResolvedPosition::explicitGridSizeForSide(const RenderStyle& gridContainerStyle, GridPositionSide side)
137 {
138     return (side == ColumnStartSide || side == ColumnEndSide) ? explicitGridColumnCount(gridContainerStyle) : explicitGridRowCount(gridContainerStyle);
139 }
140 
resolveNamedGridLinePositionFromStyle(const RenderStyle & gridContainerStyle,const GridPosition & position,GridPositionSide side)141 GridResolvedPosition GridResolvedPosition::resolveNamedGridLinePositionFromStyle(const RenderStyle& gridContainerStyle, const GridPosition& position, GridPositionSide side)
142 {
143     ASSERT(!position.namedGridLine().isNull());
144 
145     const NamedGridLinesMap& gridLinesNames = gridLinesForSide(gridContainerStyle, side);
146     NamedGridLinesMap::const_iterator it = gridLinesNames.find(position.namedGridLine());
147     if (it == gridLinesNames.end()) {
148         if (position.isPositive())
149             return GridResolvedPosition(0);
150         const size_t lastLine = explicitGridSizeForSide(gridContainerStyle, side);
151         return adjustGridPositionForSide(lastLine, side);
152     }
153 
154     size_t namedGridLineIndex;
155     if (position.isPositive())
156         namedGridLineIndex = std::min<size_t>(position.integerPosition(), it->value.size()) - 1;
157     else
158         namedGridLineIndex = std::max<int>(it->value.size() - abs(position.integerPosition()), 0);
159     return adjustGridPositionForSide(it->value[namedGridLineIndex], side);
160 }
161 
resolveGridPositionFromStyle(const RenderStyle & gridContainerStyle,const GridPosition & position,GridPositionSide side)162 GridResolvedPosition GridResolvedPosition::resolveGridPositionFromStyle(const RenderStyle& gridContainerStyle, const GridPosition& position, GridPositionSide side)
163 {
164     switch (position.type()) {
165     case ExplicitPosition: {
166         ASSERT(position.integerPosition());
167 
168         if (!position.namedGridLine().isNull())
169             return resolveNamedGridLinePositionFromStyle(gridContainerStyle, position, side);
170 
171         // Handle <integer> explicit position.
172         if (position.isPositive())
173             return adjustGridPositionForSide(position.integerPosition() - 1, side);
174 
175         size_t resolvedPosition = abs(position.integerPosition()) - 1;
176         const size_t endOfTrack = explicitGridSizeForSide(gridContainerStyle, side);
177 
178         // Per http://lists.w3.org/Archives/Public/www-style/2013Mar/0589.html, we clamp negative value to the first line.
179         if (endOfTrack < resolvedPosition)
180             return GridResolvedPosition(0);
181 
182         return adjustGridPositionForSide(endOfTrack - resolvedPosition, side);
183     }
184     case NamedGridAreaPosition:
185     {
186         // First attempt to match the grid area’s edge to a named grid area: if there is a named line with the name
187         // ''<custom-ident>-start (for grid-*-start) / <custom-ident>-end'' (for grid-*-end), contributes the first such
188         // line to the grid item’s placement.
189         String namedGridLine = position.namedGridLine();
190         ASSERT(isValidNamedLineOrArea(namedGridLine, gridContainerStyle, side));
191 
192         const NamedGridLinesMap& gridLineNames = gridLinesForSide(gridContainerStyle, side);
193         NamedGridLinesMap::const_iterator implicitLineIter = gridLineNames.find(implicitNamedGridLineForSide(namedGridLine, side));
194         if (implicitLineIter != gridLineNames.end())
195             return adjustGridPositionForSide(implicitLineIter->value[0], side);
196 
197         // Otherwise, if there is a named line with the specified name, contributes the first such line to the grid
198         // item’s placement.
199         NamedGridLinesMap::const_iterator explicitLineIter = gridLineNames.find(namedGridLine);
200         if (explicitLineIter != gridLineNames.end())
201             return adjustGridPositionForSide(explicitLineIter->value[0], side);
202 
203         // If none of the above works specs mandate us to treat it as auto BUT we should have detected it before calling
204         // this function in GridResolvedPosition::resolveGridPositionsFromStyle(). We should be also covered by the
205         // ASSERT at the beginning of this block.
206         ASSERT_NOT_REACHED();
207         return GridResolvedPosition(0);
208     }
209     case AutoPosition:
210     case SpanPosition:
211         // 'auto' and span depend on the opposite position for resolution (e.g. grid-row: auto / 1 or grid-column: span 3 / "myHeader").
212         ASSERT_NOT_REACHED();
213         return GridResolvedPosition(0);
214     }
215     ASSERT_NOT_REACHED();
216     return GridResolvedPosition(0);
217 }
218 
resolveGridPositionAgainstOppositePosition(const RenderStyle & gridContainerStyle,const GridResolvedPosition & resolvedOppositePosition,const GridPosition & position,GridPositionSide side)219 PassOwnPtr<GridSpan> GridResolvedPosition::resolveGridPositionAgainstOppositePosition(const RenderStyle& gridContainerStyle, const GridResolvedPosition& resolvedOppositePosition, const GridPosition& position, GridPositionSide side)
220 {
221     if (position.isAuto())
222         return GridSpan::create(resolvedOppositePosition, resolvedOppositePosition);
223 
224     ASSERT(position.isSpan());
225     ASSERT(position.spanPosition() > 0);
226 
227     if (!position.namedGridLine().isNull()) {
228         // span 2 'c' -> we need to find the appropriate grid line before / after our opposite position.
229         return resolveNamedGridLinePositionAgainstOppositePosition(gridContainerStyle, resolvedOppositePosition, position, side);
230     }
231 
232     return GridSpan::createWithSpanAgainstOpposite(resolvedOppositePosition, position, side);
233 }
234 
resolveNamedGridLinePositionAgainstOppositePosition(const RenderStyle & gridContainerStyle,const GridResolvedPosition & resolvedOppositePosition,const GridPosition & position,GridPositionSide side)235 PassOwnPtr<GridSpan> GridResolvedPosition::resolveNamedGridLinePositionAgainstOppositePosition(const RenderStyle& gridContainerStyle, const GridResolvedPosition& resolvedOppositePosition, const GridPosition& position, GridPositionSide side)
236 {
237     ASSERT(position.isSpan());
238     ASSERT(!position.namedGridLine().isNull());
239     // Negative positions are not allowed per the specification and should have been handled during parsing.
240     ASSERT(position.spanPosition() > 0);
241 
242     const NamedGridLinesMap& gridLinesNames = gridLinesForSide(gridContainerStyle, side);
243     NamedGridLinesMap::const_iterator it = gridLinesNames.find(position.namedGridLine());
244 
245     // 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).
246     // See http://lists.w3.org/Archives/Public/www-style/2013Jun/0394.html.
247     if (it == gridLinesNames.end())
248         return GridSpan::create(resolvedOppositePosition, resolvedOppositePosition);
249 
250     return GridSpan::createWithNamedSpanAgainstOpposite(resolvedOppositePosition, position, side, it->value);
251 }
252 
253 } // namespace WebCore
254