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/. */
6
7 #include "GridLines.h"
8
9 #include "GridDimension.h"
10 #include "GridLine.h"
11 #include "mozilla/dom/GridBinding.h"
12 #include "mozilla/dom/GridArea.h"
13 #include "nsGridContainerFrame.h"
14
15 namespace mozilla::dom {
16
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridLines,mParent,mLines)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)
23 NS_INTERFACE_MAP_END
24
25 GridLines::GridLines(GridDimension* aParent) : mParent(aParent) {
26 MOZ_ASSERT(aParent, "Should never be instantiated with a null GridDimension");
27 }
28
29 GridLines::~GridLines() = default;
30
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)31 JSObject* GridLines::WrapObject(JSContext* aCx,
32 JS::Handle<JSObject*> aGivenProto) {
33 return GridLines_Binding::Wrap(aCx, this, aGivenProto);
34 }
35
Length() const36 uint32_t GridLines::Length() const { return mLines.Length(); }
37
Item(uint32_t aIndex)38 GridLine* GridLines::Item(uint32_t aIndex) {
39 return mLines.SafeElementAt(aIndex);
40 }
41
IndexedGetter(uint32_t aIndex,bool & aFound)42 GridLine* GridLines::IndexedGetter(uint32_t aIndex, bool& aFound) {
43 aFound = aIndex < mLines.Length();
44 if (!aFound) {
45 return nullptr;
46 }
47 return mLines[aIndex];
48 }
49
AddLineNameIfNotPresent(nsTArray<RefPtr<nsAtom>> & aLineNames,nsAtom * aName)50 static void AddLineNameIfNotPresent(nsTArray<RefPtr<nsAtom>>& aLineNames,
51 nsAtom* aName) {
52 if (!aLineNames.Contains(aName)) {
53 aLineNames.AppendElement(aName);
54 }
55 }
56
AddLineNamesIfNotPresent(nsTArray<RefPtr<nsAtom>> & aLineNames,const nsTArray<RefPtr<nsAtom>> & aNames)57 static void AddLineNamesIfNotPresent(nsTArray<RefPtr<nsAtom>>& aLineNames,
58 const nsTArray<RefPtr<nsAtom>>& aNames) {
59 for (const auto& name : aNames) {
60 AddLineNameIfNotPresent(aLineNames, name);
61 }
62 }
63
SetLineInfo(const ComputedGridTrackInfo * aTrackInfo,const ComputedGridLineInfo * aLineInfo,const nsTArray<RefPtr<GridArea>> & aAreas,bool aIsRow)64 void GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo,
65 const ComputedGridLineInfo* aLineInfo,
66 const nsTArray<RefPtr<GridArea>>& aAreas,
67 bool aIsRow) {
68 MOZ_ASSERT(aLineInfo);
69 mLines.Clear();
70
71 if (!aTrackInfo) {
72 return;
73 }
74
75 uint32_t lineCount =
76 aTrackInfo->mEndFragmentTrack - aTrackInfo->mStartFragmentTrack + 1;
77
78 // If there is at least one track, line count is one more
79 // than the number of tracks.
80 if (lineCount > 0) {
81 nscoord lastTrackEdge = 0;
82 nscoord startOfNextTrack;
83 uint32_t repeatIndex = 0;
84 uint32_t numRepeatTracks = aTrackInfo->mRemovedRepeatTracks.Length();
85 uint32_t numAddedLines = 0;
86
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
91 // have been removed.
92 uint32_t leadingTrackCount =
93 aTrackInfo->mNumLeadingImplicitTracks + aTrackInfo->mNumExplicitTracks;
94 if (numRepeatTracks > 0) {
95 for (auto& removedTrack : aTrackInfo->mRemovedRepeatTracks) {
96 if (removedTrack) {
97 ++leadingTrackCount;
98 }
99 }
100 }
101
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;
107
108 startOfNextTrack = (i < aTrackInfo->mEndFragmentTrack)
109 ? aTrackInfo->mPositions[i]
110 : lastTrackEdge;
111
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
118 // normal browsing.
119 const nsTArray<RefPtr<nsAtom>>& possiblyDuplicateLineNames(
120 aLineInfo->mNames.SafeElementAt(i, nsTArray<RefPtr<nsAtom>>()));
121
122 nsTArray<RefPtr<nsAtom>> lineNames;
123 AddLineNamesIfNotPresent(lineNames, possiblyDuplicateLineNames);
124
125 // Add in names from grid areas where this line is used as a boundary.
126 for (auto area : aAreas) {
127 // We specifically ignore line names from implicitly named areas,
128 // because it can be confusing for designers who might naturally use
129 // a named line of "-start" or "-end" and create an implicit named
130 // area without meaning to.
131 if (area->Type() == GridDeclaration::Implicit) {
132 continue;
133 }
134
135 bool haveNameToAdd = false;
136 nsAutoString nameToAdd;
137 area->GetName(nameToAdd);
138 if (aIsRow) {
139 if (area->RowStart() == line1Index) {
140 haveNameToAdd = true;
141 nameToAdd.AppendLiteral("-start");
142 } else if (area->RowEnd() == line1Index) {
143 haveNameToAdd = true;
144 nameToAdd.AppendLiteral("-end");
145 }
146 } else {
147 if (area->ColumnStart() == line1Index) {
148 haveNameToAdd = true;
149 nameToAdd.AppendLiteral("-start");
150 } else if (area->ColumnEnd() == line1Index) {
151 haveNameToAdd = true;
152 nameToAdd.AppendLiteral("-end");
153 }
154 }
155
156 if (haveNameToAdd) {
157 RefPtr<nsAtom> name = NS_Atomize(nameToAdd);
158 AddLineNameIfNotPresent(lineNames, name);
159 }
160 }
161
162 if (i >= (aTrackInfo->mRepeatFirstTrack +
163 aTrackInfo->mNumLeadingImplicitTracks) &&
164 repeatIndex < numRepeatTracks) {
165 numAddedLines += AppendRemovedAutoFits(
166 aTrackInfo, aLineInfo, lastTrackEdge, repeatIndex, numRepeatTracks,
167 leadingTrackCount, lineNames);
168 }
169
170 // If this line is the one that ends a repeat, then add
171 // in the mNamesFollowingRepeat names from aLineInfo.
172 if (numRepeatTracks > 0 && i == (aTrackInfo->mRepeatFirstTrack +
173 aTrackInfo->mNumLeadingImplicitTracks +
174 numRepeatTracks - numAddedLines)) {
175 AddLineNamesIfNotPresent(lineNames, aLineInfo->mNamesFollowingRepeat);
176 }
177
178 RefPtr<GridLine> line = new GridLine(this);
179 mLines.AppendElement(line);
180 MOZ_ASSERT(line1Index > 0, "line1Index must be positive.");
181 bool isBeforeFirstExplicit =
182 (line1Index <= aTrackInfo->mNumLeadingImplicitTracks);
183 bool isAfterLastExplicit = line1Index > (leadingTrackCount + 1);
184 // Calculate an actionable line number for this line, that could be used
185 // in a css grid property to align a grid item or area at that line.
186 // For implicit lines that appear before line 1, report a number of 0.
187 // We can't report negative indexes, because those have a different
188 // meaning in the css grid spec (negative indexes are negative-1-based
189 // from the end of the grid decreasing towards the front).
190 uint32_t lineNumber = isBeforeFirstExplicit
191 ? 0
192 : (line1Index + numAddedLines -
193 aTrackInfo->mNumLeadingImplicitTracks);
194
195 // The negativeNumber is counted back from the leadingTrackCount.
196 int32_t lineNegativeNumber =
197 isAfterLastExplicit
198 ? 0
199 : (line1Index + numAddedLines - (leadingTrackCount + 2));
200 GridDeclaration lineType = (isBeforeFirstExplicit || isAfterLastExplicit)
201 ? GridDeclaration::Implicit
202 : GridDeclaration::Explicit;
203 line->SetLineValues(
204 lineNames, nsPresContext::AppUnitsToDoubleCSSPixels(lastTrackEdge),
205 nsPresContext::AppUnitsToDoubleCSSPixels(startOfNextTrack -
206 lastTrackEdge),
207 lineNumber, lineNegativeNumber, lineType);
208
209 if (i < aTrackInfo->mEndFragmentTrack) {
210 lastTrackEdge = aTrackInfo->mPositions[i] + aTrackInfo->mSizes[i];
211 }
212 }
213
214 // Define a function that gets the mLines index for a given line number.
215 // This is necessary since it's possible for a line number to not be
216 // represented in mLines. If this is the case, then return -1.
217 const int32_t lineCount = mLines.Length();
218 const uint32_t lastLineNumber = mLines[lineCount - 1]->Number();
219 auto IndexForLineNumber =
220 [lineCount, lastLineNumber](uint32_t aLineNumber) -> int32_t {
221 if (lastLineNumber == 0) {
222 // None of the lines have addressable numbers, so none of them can have
223 // aLineNumber
224 return -1;
225 }
226
227 int32_t possibleIndex = (int32_t)aLineNumber - 1;
228 if (possibleIndex < 0 || possibleIndex > lineCount - 1) {
229 // aLineNumber is not represented in mLines.
230 return -1;
231 }
232
233 return possibleIndex;
234 };
235
236 // Post-processing loop for implicit grid areas.
237 for (const auto& area : aAreas) {
238 if (area->Type() == GridDeclaration::Implicit) {
239 // Get the appropriate indexes for the area's start and end lines as
240 // they are represented in mLines.
241 int32_t startIndex =
242 IndexForLineNumber(aIsRow ? area->RowStart() : area->ColumnStart());
243 int32_t endIndex =
244 IndexForLineNumber(aIsRow ? area->RowEnd() : area->ColumnEnd());
245
246 // If both start and end indexes are -1, then stop here since we cannot
247 // reason about the naming for either lines.
248 if (startIndex < 0 && endIndex < 0) {
249 break;
250 }
251
252 // Get the "-start" and "-end" line names of the grid area.
253 nsAutoString startLineName;
254 area->GetName(startLineName);
255 startLineName.AppendLiteral("-start");
256 nsAutoString endLineName;
257 area->GetName(endLineName);
258 endLineName.AppendLiteral("-end");
259
260 // Get the list of existing line names for the start and end of the grid
261 // area. In the case where one of the start or end indexes are -1, use a
262 // dummy line as a substitute for the start/end line.
263 RefPtr<GridLine> dummyLine = new GridLine(this);
264 RefPtr<GridLine> areaStartLine =
265 startIndex > -1 ? mLines[startIndex] : dummyLine;
266 nsTArray<RefPtr<nsAtom>> startLineNames(areaStartLine->Names().Clone());
267
268 RefPtr<GridLine> areaEndLine =
269 endIndex > -1 ? mLines[endIndex] : dummyLine;
270 nsTArray<RefPtr<nsAtom>> endLineNames(areaEndLine->Names().Clone());
271
272 RefPtr<nsAtom> start = NS_Atomize(startLineName);
273 RefPtr<nsAtom> end = NS_Atomize(endLineName);
274 if (startLineNames.Contains(end) || endLineNames.Contains(start)) {
275 // Add the reversed line names.
276 AddLineNameIfNotPresent(startLineNames, end);
277 AddLineNameIfNotPresent(endLineNames, start);
278 } else {
279 // Add the normal line names.
280 AddLineNameIfNotPresent(startLineNames, start);
281 AddLineNameIfNotPresent(endLineNames, end);
282 }
283
284 areaStartLine->SetLineNames(startLineNames);
285 areaEndLine->SetLineNames(endLineNames);
286 }
287 }
288 }
289 }
290
AppendRemovedAutoFits(const ComputedGridTrackInfo * aTrackInfo,const ComputedGridLineInfo * aLineInfo,nscoord aLastTrackEdge,uint32_t & aRepeatIndex,uint32_t aNumRepeatTracks,uint32_t aNumLeadingTracks,nsTArray<RefPtr<nsAtom>> & aLineNames)291 uint32_t GridLines::AppendRemovedAutoFits(
292 const ComputedGridTrackInfo* aTrackInfo,
293 const ComputedGridLineInfo* aLineInfo, nscoord aLastTrackEdge,
294 uint32_t& aRepeatIndex, uint32_t aNumRepeatTracks,
295 uint32_t aNumLeadingTracks, nsTArray<RefPtr<nsAtom>>& aLineNames) {
296 bool extractedExplicitLineNames = false;
297 nsTArray<RefPtr<nsAtom>> explicitLineNames;
298 uint32_t linesAdded = 0;
299 while (aRepeatIndex < aNumRepeatTracks &&
300 aTrackInfo->mRemovedRepeatTracks[aRepeatIndex]) {
301 // If this is not the very first call to this function, and if we
302 // haven't already added a line this call, pull all the explicit
303 // names to pass along to the next line that will be added after
304 // this function completes.
305 if (aRepeatIndex > 0 && linesAdded == 0) {
306 // Find the names that didn't match the before or after names,
307 // and extract them.
308 for (const auto& name : aLineNames) {
309 if (!aLineInfo->mNamesBefore.Contains(name) &&
310 !aLineInfo->mNamesAfter.Contains(name)) {
311 explicitLineNames.AppendElement(name);
312 }
313 }
314 for (const auto& extractedName : explicitLineNames) {
315 aLineNames.RemoveElement(extractedName);
316 }
317 extractedExplicitLineNames = true;
318 }
319
320 AddLineNamesIfNotPresent(aLineNames, aLineInfo->mNamesBefore);
321
322 RefPtr<GridLine> line = new GridLine(this);
323 mLines.AppendElement(line);
324
325 // Time to calculate the line numbers. For the positive numbers
326 // we count with a 1-based index from mRepeatFirstTrack. Although
327 // this number is the index of the first repeat track AFTER all
328 // the leading implicit tracks, that's still what we want since
329 // all those leading implicit tracks have line number 0.
330 uint32_t lineNumber = aTrackInfo->mRepeatFirstTrack + aRepeatIndex + 1;
331
332 // The negative number does have to account for the leading
333 // implicit tracks. We've been passed aNumLeadingTracks which is
334 // the total of the leading implicit tracks plus the explicit
335 // tracks. So all we have to do is subtract that number plus one
336 // from the 0-based index of this track.
337 int32_t lineNegativeNumber =
338 (aTrackInfo->mNumLeadingImplicitTracks + aTrackInfo->mRepeatFirstTrack +
339 aRepeatIndex) -
340 (aNumLeadingTracks + 1);
341 line->SetLineValues(
342 aLineNames, nsPresContext::AppUnitsToDoubleCSSPixels(aLastTrackEdge),
343 nsPresContext::AppUnitsToDoubleCSSPixels(0), lineNumber,
344 lineNegativeNumber, GridDeclaration::Explicit);
345
346 // No matter what, the next line should have the after names associated
347 // with it. If we go through the loop again, the before names will also
348 // be added.
349 aLineNames = aLineInfo->mNamesAfter.Clone();
350 aRepeatIndex++;
351
352 linesAdded++;
353 }
354
355 aRepeatIndex++;
356
357 if (extractedExplicitLineNames) {
358 // Pass on the explicit names we saved to the next explicit line.
359 AddLineNamesIfNotPresent(aLineNames, explicitLineNames);
360 }
361
362 // If we haven't finished adding auto-repeated tracks, then we need to put
363 // back the before names, in case we cleared them above.
364 if (aRepeatIndex < aNumRepeatTracks) {
365 AddLineNamesIfNotPresent(aLineNames, aLineInfo->mNamesBefore);
366 }
367
368 return linesAdded;
369 }
370
371 } // namespace mozilla::dom
372