GridLines.cpp (14816B)
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/GridArea.h" 12 #include "mozilla/dom/GridBinding.h" 13 #include "nsGridContainerFrame.h" 14 15 namespace mozilla::dom { 16 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 31 JSObject* GridLines::WrapObject(JSContext* aCx, 32 JS::Handle<JSObject*> aGivenProto) { 33 return GridLines_Binding::Wrap(aCx, this, aGivenProto); 34 } 35 36 uint32_t GridLines::Length() const { return mLines.Length(); } 37 38 GridLine* GridLines::Item(uint32_t aIndex) { 39 return mLines.SafeElementAt(aIndex); 40 } 41 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 50 static void AddLineNameIfNotPresent(nsTArray<RefPtr<nsAtom>>& aLineNames, 51 nsAtom* aName) { 52 if (!aLineNames.Contains(aName)) { 53 aLineNames.AppendElement(aName); 54 } 55 } 56 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 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 nsTArray<RefPtr<nsAtom>> empty{}; 120 const nsTArray<RefPtr<nsAtom>>& possiblyDuplicateLineNames( 121 aLineInfo->mNames.SafeElementAt(i, empty)); 122 123 nsTArray<RefPtr<nsAtom>> lineNames; 124 AddLineNamesIfNotPresent(lineNames, possiblyDuplicateLineNames); 125 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) { 133 continue; 134 } 135 136 bool haveNameToAdd = false; 137 nsAutoString nameToAdd; 138 area->GetName(nameToAdd); 139 if (aIsRow) { 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"); 146 } 147 } else { 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"); 154 } 155 } 156 157 if (haveNameToAdd) { 158 RefPtr<nsAtom> name = NS_Atomize(nameToAdd); 159 AddLineNameIfNotPresent(lineNames, name); 160 } 161 } 162 163 if (i >= (aTrackInfo->mRepeatFirstTrack + 164 aTrackInfo->mNumLeadingImplicitTracks) && 165 repeatIndex < numRepeatTracks) { 166 numAddedLines += AppendRemovedAutoFits( 167 aTrackInfo, aLineInfo, lastTrackEdge, repeatIndex, numRepeatTracks, 168 leadingTrackCount, lineNames); 169 } 170 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); 177 } 178 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 192 ? 0 193 : (line1Index + numAddedLines - 194 aTrackInfo->mNumLeadingImplicitTracks); 195 196 // The negativeNumber is counted back from the leadingTrackCount. 197 int32_t lineNegativeNumber = 198 isAfterLastExplicit 199 ? 0 200 : (line1Index + numAddedLines - (leadingTrackCount + 2)); 201 GridDeclaration lineType = (isBeforeFirstExplicit || isAfterLastExplicit) 202 ? GridDeclaration::Implicit 203 : GridDeclaration::Explicit; 204 line->SetLineValues( 205 lineNames, nsPresContext::AppUnitsToDoubleCSSPixels(lastTrackEdge), 206 nsPresContext::AppUnitsToDoubleCSSPixels(startOfNextTrack - 207 lastTrackEdge), 208 lineNumber, lineNegativeNumber, lineType); 209 210 if (i < aTrackInfo->mEndFragmentTrack) { 211 lastTrackEdge = aTrackInfo->mPositions[i] + aTrackInfo->mSizes[i]; 212 } 213 } 214 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 224 // aLineNumber 225 return -1; 226 } 227 228 int32_t possibleIndex = (int32_t)aLineNumber - 1; 229 if (possibleIndex < 0 || possibleIndex > lineCount - 1) { 230 // aLineNumber is not represented in mLines. 231 return -1; 232 } 233 234 return possibleIndex; 235 }; 236 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. 242 int32_t startIndex = 243 IndexForLineNumber(aIsRow ? area->RowStart() : area->ColumnStart()); 244 int32_t endIndex = 245 IndexForLineNumber(aIsRow ? area->RowEnd() : area->ColumnEnd()); 246 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) { 250 break; 251 } 252 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"); 260 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()); 268 269 RefPtr<GridLine> areaEndLine = 270 endIndex > -1 ? mLines[endIndex] : dummyLine; 271 nsTArray<RefPtr<nsAtom>> endLineNames(areaEndLine->Names().Clone()); 272 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); 279 } else { 280 // Add the normal line names. 281 AddLineNameIfNotPresent(startLineNames, start); 282 AddLineNameIfNotPresent(endLineNames, end); 283 } 284 285 areaStartLine->SetLineNames(startLineNames); 286 areaEndLine->SetLineNames(endLineNames); 287 } 288 } 289 } 290 } 291 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, 308 // and extract them. 309 for (const auto& name : aLineNames) { 310 if (!aLineInfo->mNamesBefore.Contains(name) && 311 !aLineInfo->mNamesAfter.Contains(name)) { 312 explicitLineNames.AppendElement(name); 313 } 314 } 315 for (const auto& extractedName : explicitLineNames) { 316 aLineNames.RemoveElement(extractedName); 317 } 318 extractedExplicitLineNames = true; 319 } 320 321 AddLineNamesIfNotPresent(aLineNames, aLineInfo->mNamesBefore); 322 323 RefPtr<GridLine> line = new GridLine(this); 324 mLines.AppendElement(line); 325 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; 332 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 + 340 aRepeatIndex) - 341 (aNumLeadingTracks + 1); 342 line->SetLineValues( 343 aLineNames, nsPresContext::AppUnitsToDoubleCSSPixels(aLastTrackEdge), 344 nsPresContext::AppUnitsToDoubleCSSPixels(0), lineNumber, 345 lineNegativeNumber, GridDeclaration::Explicit); 346 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 349 // be added. 350 aLineNames = aLineInfo->mNamesAfter.Clone(); 351 aRepeatIndex++; 352 353 linesAdded++; 354 } 355 356 aRepeatIndex++; 357 358 if (extractedExplicitLineNames) { 359 // Pass on the explicit names we saved to the next explicit line. 360 AddLineNamesIfNotPresent(aLineNames, explicitLineNames); 361 } 362 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); 367 } 368 369 return linesAdded; 370 } 371 372 } // namespace mozilla::dom