1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "GridDimension.h"
11 #include "mozilla/dom/GridBinding.h"
12 #include "mozilla/dom/GridArea.h"
13 #include "nsGridContainerFrame.h"
15 namespace mozilla::dom
{
17 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridLines
, mParent
, mLines
)
18 NS_IMPL_CYCLE_COLLECTING_ADDREF(GridLines
)
19 NS_IMPL_CYCLE_COLLECTING_RELEASE(GridLines
)
20 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridLines
)
21 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
22 NS_INTERFACE_MAP_ENTRY(nsISupports
)
25 GridLines::GridLines(GridDimension
* aParent
) : mParent(aParent
) {
26 MOZ_ASSERT(aParent
, "Should never be instantiated with a null GridDimension");
29 GridLines::~GridLines() = default;
31 JSObject
* GridLines::WrapObject(JSContext
* aCx
,
32 JS::Handle
<JSObject
*> aGivenProto
) {
33 return GridLines_Binding::Wrap(aCx
, this, aGivenProto
);
36 uint32_t GridLines::Length() const { return mLines
.Length(); }
38 GridLine
* GridLines::Item(uint32_t aIndex
) {
39 return mLines
.SafeElementAt(aIndex
);
42 GridLine
* GridLines::IndexedGetter(uint32_t aIndex
, bool& aFound
) {
43 aFound
= aIndex
< mLines
.Length();
47 return mLines
[aIndex
];
50 static void AddLineNameIfNotPresent(nsTArray
<RefPtr
<nsAtom
>>& aLineNames
,
52 if (!aLineNames
.Contains(aName
)) {
53 aLineNames
.AppendElement(aName
);
57 static void AddLineNamesIfNotPresent(nsTArray
<RefPtr
<nsAtom
>>& aLineNames
,
58 const nsTArray
<RefPtr
<nsAtom
>>& aNames
) {
59 for (const auto& name
: aNames
) {
60 AddLineNameIfNotPresent(aLineNames
, name
);
64 void GridLines::SetLineInfo(const ComputedGridTrackInfo
* aTrackInfo
,
65 const ComputedGridLineInfo
* aLineInfo
,
66 const nsTArray
<RefPtr
<GridArea
>>& aAreas
,
68 MOZ_ASSERT(aLineInfo
);
76 aTrackInfo
->mEndFragmentTrack
- aTrackInfo
->mStartFragmentTrack
+ 1;
78 // If there is at least one track, line count is one more
79 // than the number of tracks.
81 nscoord lastTrackEdge
= 0;
82 nscoord startOfNextTrack
;
83 uint32_t repeatIndex
= 0;
84 uint32_t numRepeatTracks
= aTrackInfo
->mRemovedRepeatTracks
.Length();
85 uint32_t numAddedLines
= 0;
87 // For the calculation of negative line numbers, we need to know
88 // the total number of leading implicit and explicit tracks.
89 // This might be different from the number of tracks sizes in
90 // aTrackInfo, because some of those tracks may be auto-fits that
92 uint32_t leadingTrackCount
=
93 aTrackInfo
->mNumLeadingImplicitTracks
+ aTrackInfo
->mNumExplicitTracks
;
94 if (numRepeatTracks
> 0) {
95 for (auto& removedTrack
: aTrackInfo
->mRemovedRepeatTracks
) {
102 for (uint32_t i
= aTrackInfo
->mStartFragmentTrack
;
103 i
< aTrackInfo
->mEndFragmentTrack
+ 1; i
++) {
104 // Since line indexes are 1-based, calculate a 1-based value
105 // for this track to simplify some calculations.
106 const uint32_t line1Index
= i
+ 1;
108 startOfNextTrack
= (i
< aTrackInfo
->mEndFragmentTrack
)
109 ? aTrackInfo
->mPositions
[i
]
112 // Get the line names for the current line. aLineInfo->mNames
113 // may contain duplicate names. This is intentional, since grid
114 // layout works fine with duplicate names, and we don't want to
115 // detect and remove duplicates in layout since it is an O(n^2)
116 // problem. We do the work here since this is only run when
117 // requested by devtools, and slowness here will not affect
119 nsTArray
<RefPtr
<nsAtom
>> empty
{};
120 const nsTArray
<RefPtr
<nsAtom
>>& possiblyDuplicateLineNames(
121 aLineInfo
->mNames
.SafeElementAt(i
, empty
));
123 nsTArray
<RefPtr
<nsAtom
>> lineNames
;
124 AddLineNamesIfNotPresent(lineNames
, possiblyDuplicateLineNames
);
126 // Add in names from grid areas where this line is used as a boundary.
127 for (auto area
: aAreas
) {
128 // We specifically ignore line names from implicitly named areas,
129 // because it can be confusing for designers who might naturally use
130 // a named line of "-start" or "-end" and create an implicit named
131 // area without meaning to.
132 if (area
->Type() == GridDeclaration::Implicit
) {
136 bool haveNameToAdd
= false;
137 nsAutoString nameToAdd
;
138 area
->GetName(nameToAdd
);
140 if (area
->RowStart() == line1Index
) {
141 haveNameToAdd
= true;
142 nameToAdd
.AppendLiteral("-start");
143 } else if (area
->RowEnd() == line1Index
) {
144 haveNameToAdd
= true;
145 nameToAdd
.AppendLiteral("-end");
148 if (area
->ColumnStart() == line1Index
) {
149 haveNameToAdd
= true;
150 nameToAdd
.AppendLiteral("-start");
151 } else if (area
->ColumnEnd() == line1Index
) {
152 haveNameToAdd
= true;
153 nameToAdd
.AppendLiteral("-end");
158 RefPtr
<nsAtom
> name
= NS_Atomize(nameToAdd
);
159 AddLineNameIfNotPresent(lineNames
, name
);
163 if (i
>= (aTrackInfo
->mRepeatFirstTrack
+
164 aTrackInfo
->mNumLeadingImplicitTracks
) &&
165 repeatIndex
< numRepeatTracks
) {
166 numAddedLines
+= AppendRemovedAutoFits(
167 aTrackInfo
, aLineInfo
, lastTrackEdge
, repeatIndex
, numRepeatTracks
,
168 leadingTrackCount
, lineNames
);
171 // If this line is the one that ends a repeat, then add
172 // in the mNamesFollowingRepeat names from aLineInfo.
173 if (numRepeatTracks
> 0 && i
== (aTrackInfo
->mRepeatFirstTrack
+
174 aTrackInfo
->mNumLeadingImplicitTracks
+
175 numRepeatTracks
- numAddedLines
)) {
176 AddLineNamesIfNotPresent(lineNames
, aLineInfo
->mNamesFollowingRepeat
);
179 RefPtr
<GridLine
> line
= new GridLine(this);
180 mLines
.AppendElement(line
);
181 MOZ_ASSERT(line1Index
> 0, "line1Index must be positive.");
182 bool isBeforeFirstExplicit
=
183 (line1Index
<= aTrackInfo
->mNumLeadingImplicitTracks
);
184 bool isAfterLastExplicit
= line1Index
> (leadingTrackCount
+ 1);
185 // Calculate an actionable line number for this line, that could be used
186 // in a css grid property to align a grid item or area at that line.
187 // For implicit lines that appear before line 1, report a number of 0.
188 // We can't report negative indexes, because those have a different
189 // meaning in the css grid spec (negative indexes are negative-1-based
190 // from the end of the grid decreasing towards the front).
191 uint32_t lineNumber
= isBeforeFirstExplicit
193 : (line1Index
+ numAddedLines
-
194 aTrackInfo
->mNumLeadingImplicitTracks
);
196 // The negativeNumber is counted back from the leadingTrackCount.
197 int32_t lineNegativeNumber
=
200 : (line1Index
+ numAddedLines
- (leadingTrackCount
+ 2));
201 GridDeclaration lineType
= (isBeforeFirstExplicit
|| isAfterLastExplicit
)
202 ? GridDeclaration::Implicit
203 : GridDeclaration::Explicit
;
205 lineNames
, nsPresContext::AppUnitsToDoubleCSSPixels(lastTrackEdge
),
206 nsPresContext::AppUnitsToDoubleCSSPixels(startOfNextTrack
-
208 lineNumber
, lineNegativeNumber
, lineType
);
210 if (i
< aTrackInfo
->mEndFragmentTrack
) {
211 lastTrackEdge
= aTrackInfo
->mPositions
[i
] + aTrackInfo
->mSizes
[i
];
215 // Define a function that gets the mLines index for a given line number.
216 // This is necessary since it's possible for a line number to not be
217 // represented in mLines. If this is the case, then return -1.
218 const int32_t lineCount
= mLines
.Length();
219 const uint32_t lastLineNumber
= mLines
[lineCount
- 1]->Number();
220 auto IndexForLineNumber
=
221 [lineCount
, lastLineNumber
](uint32_t aLineNumber
) -> int32_t {
222 if (lastLineNumber
== 0) {
223 // None of the lines have addressable numbers, so none of them can have
228 int32_t possibleIndex
= (int32_t)aLineNumber
- 1;
229 if (possibleIndex
< 0 || possibleIndex
> lineCount
- 1) {
230 // aLineNumber is not represented in mLines.
234 return possibleIndex
;
237 // Post-processing loop for implicit grid areas.
238 for (const auto& area
: aAreas
) {
239 if (area
->Type() == GridDeclaration::Implicit
) {
240 // Get the appropriate indexes for the area's start and end lines as
241 // they are represented in mLines.
243 IndexForLineNumber(aIsRow
? area
->RowStart() : area
->ColumnStart());
245 IndexForLineNumber(aIsRow
? area
->RowEnd() : area
->ColumnEnd());
247 // If both start and end indexes are -1, then stop here since we cannot
248 // reason about the naming for either lines.
249 if (startIndex
< 0 && endIndex
< 0) {
253 // Get the "-start" and "-end" line names of the grid area.
254 nsAutoString startLineName
;
255 area
->GetName(startLineName
);
256 startLineName
.AppendLiteral("-start");
257 nsAutoString endLineName
;
258 area
->GetName(endLineName
);
259 endLineName
.AppendLiteral("-end");
261 // Get the list of existing line names for the start and end of the grid
262 // area. In the case where one of the start or end indexes are -1, use a
263 // dummy line as a substitute for the start/end line.
264 RefPtr
<GridLine
> dummyLine
= new GridLine(this);
265 RefPtr
<GridLine
> areaStartLine
=
266 startIndex
> -1 ? mLines
[startIndex
] : dummyLine
;
267 nsTArray
<RefPtr
<nsAtom
>> startLineNames(areaStartLine
->Names().Clone());
269 RefPtr
<GridLine
> areaEndLine
=
270 endIndex
> -1 ? mLines
[endIndex
] : dummyLine
;
271 nsTArray
<RefPtr
<nsAtom
>> endLineNames(areaEndLine
->Names().Clone());
273 RefPtr
<nsAtom
> start
= NS_Atomize(startLineName
);
274 RefPtr
<nsAtom
> end
= NS_Atomize(endLineName
);
275 if (startLineNames
.Contains(end
) || endLineNames
.Contains(start
)) {
276 // Add the reversed line names.
277 AddLineNameIfNotPresent(startLineNames
, end
);
278 AddLineNameIfNotPresent(endLineNames
, start
);
280 // Add the normal line names.
281 AddLineNameIfNotPresent(startLineNames
, start
);
282 AddLineNameIfNotPresent(endLineNames
, end
);
285 areaStartLine
->SetLineNames(startLineNames
);
286 areaEndLine
->SetLineNames(endLineNames
);
292 uint32_t GridLines::AppendRemovedAutoFits(
293 const ComputedGridTrackInfo
* aTrackInfo
,
294 const ComputedGridLineInfo
* aLineInfo
, nscoord aLastTrackEdge
,
295 uint32_t& aRepeatIndex
, uint32_t aNumRepeatTracks
,
296 uint32_t aNumLeadingTracks
, nsTArray
<RefPtr
<nsAtom
>>& aLineNames
) {
297 bool extractedExplicitLineNames
= false;
298 nsTArray
<RefPtr
<nsAtom
>> explicitLineNames
;
299 uint32_t linesAdded
= 0;
300 while (aRepeatIndex
< aNumRepeatTracks
&&
301 aTrackInfo
->mRemovedRepeatTracks
[aRepeatIndex
]) {
302 // If this is not the very first call to this function, and if we
303 // haven't already added a line this call, pull all the explicit
304 // names to pass along to the next line that will be added after
305 // this function completes.
306 if (aRepeatIndex
> 0 && linesAdded
== 0) {
307 // Find the names that didn't match the before or after names,
309 for (const auto& name
: aLineNames
) {
310 if (!aLineInfo
->mNamesBefore
.Contains(name
) &&
311 !aLineInfo
->mNamesAfter
.Contains(name
)) {
312 explicitLineNames
.AppendElement(name
);
315 for (const auto& extractedName
: explicitLineNames
) {
316 aLineNames
.RemoveElement(extractedName
);
318 extractedExplicitLineNames
= true;
321 AddLineNamesIfNotPresent(aLineNames
, aLineInfo
->mNamesBefore
);
323 RefPtr
<GridLine
> line
= new GridLine(this);
324 mLines
.AppendElement(line
);
326 // Time to calculate the line numbers. For the positive numbers
327 // we count with a 1-based index from mRepeatFirstTrack. Although
328 // this number is the index of the first repeat track AFTER all
329 // the leading implicit tracks, that's still what we want since
330 // all those leading implicit tracks have line number 0.
331 uint32_t lineNumber
= aTrackInfo
->mRepeatFirstTrack
+ aRepeatIndex
+ 1;
333 // The negative number does have to account for the leading
334 // implicit tracks. We've been passed aNumLeadingTracks which is
335 // the total of the leading implicit tracks plus the explicit
336 // tracks. So all we have to do is subtract that number plus one
337 // from the 0-based index of this track.
338 int32_t lineNegativeNumber
=
339 (aTrackInfo
->mNumLeadingImplicitTracks
+ aTrackInfo
->mRepeatFirstTrack
+
341 (aNumLeadingTracks
+ 1);
343 aLineNames
, nsPresContext::AppUnitsToDoubleCSSPixels(aLastTrackEdge
),
344 nsPresContext::AppUnitsToDoubleCSSPixels(0), lineNumber
,
345 lineNegativeNumber
, GridDeclaration::Explicit
);
347 // No matter what, the next line should have the after names associated
348 // with it. If we go through the loop again, the before names will also
350 aLineNames
= aLineInfo
->mNamesAfter
.Clone();
358 if (extractedExplicitLineNames
) {
359 // Pass on the explicit names we saved to the next explicit line.
360 AddLineNamesIfNotPresent(aLineNames
, explicitLineNames
);
363 // If we haven't finished adding auto-repeated tracks, then we need to put
364 // back the before names, in case we cleared them above.
365 if (aRepeatIndex
< aNumRepeatTracks
) {
366 AddLineNamesIfNotPresent(aLineNames
, aLineInfo
->mNamesBefore
);
372 } // namespace mozilla::dom