diff options
Diffstat (limited to '')
-rw-r--r-- | dom/grid/GridLines.cpp | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/dom/grid/GridLines.cpp b/dom/grid/GridLines.cpp new file mode 100644 index 0000000000..4610f18eab --- /dev/null +++ b/dom/grid/GridLines.cpp @@ -0,0 +1,372 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GridLines.h" + +#include "GridDimension.h" +#include "GridLine.h" +#include "mozilla/dom/GridBinding.h" +#include "mozilla/dom/GridArea.h" +#include "nsGridContainerFrame.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridLines, mParent, mLines) +NS_IMPL_CYCLE_COLLECTING_ADDREF(GridLines) +NS_IMPL_CYCLE_COLLECTING_RELEASE(GridLines) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridLines) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +GridLines::GridLines(GridDimension* aParent) : mParent(aParent) { + MOZ_ASSERT(aParent, "Should never be instantiated with a null GridDimension"); +} + +GridLines::~GridLines() = default; + +JSObject* GridLines::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return GridLines_Binding::Wrap(aCx, this, aGivenProto); +} + +uint32_t GridLines::Length() const { return mLines.Length(); } + +GridLine* GridLines::Item(uint32_t aIndex) { + return mLines.SafeElementAt(aIndex); +} + +GridLine* GridLines::IndexedGetter(uint32_t aIndex, bool& aFound) { + aFound = aIndex < mLines.Length(); + if (!aFound) { + return nullptr; + } + return mLines[aIndex]; +} + +static void AddLineNameIfNotPresent(nsTArray<RefPtr<nsAtom>>& aLineNames, + nsAtom* aName) { + if (!aLineNames.Contains(aName)) { + aLineNames.AppendElement(aName); + } +} + +static void AddLineNamesIfNotPresent(nsTArray<RefPtr<nsAtom>>& aLineNames, + const nsTArray<RefPtr<nsAtom>>& aNames) { + for (const auto& name : aNames) { + AddLineNameIfNotPresent(aLineNames, name); + } +} + +void GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo, + const ComputedGridLineInfo* aLineInfo, + const nsTArray<RefPtr<GridArea>>& aAreas, + bool aIsRow) { + MOZ_ASSERT(aLineInfo); + mLines.Clear(); + + if (!aTrackInfo) { + return; + } + + uint32_t lineCount = + aTrackInfo->mEndFragmentTrack - aTrackInfo->mStartFragmentTrack + 1; + + // If there is at least one track, line count is one more + // than the number of tracks. + if (lineCount > 0) { + nscoord lastTrackEdge = 0; + nscoord startOfNextTrack; + uint32_t repeatIndex = 0; + uint32_t numRepeatTracks = aTrackInfo->mRemovedRepeatTracks.Length(); + uint32_t numAddedLines = 0; + + // For the calculation of negative line numbers, we need to know + // the total number of leading implicit and explicit tracks. + // This might be different from the number of tracks sizes in + // aTrackInfo, because some of those tracks may be auto-fits that + // have been removed. + uint32_t leadingTrackCount = + aTrackInfo->mNumLeadingImplicitTracks + aTrackInfo->mNumExplicitTracks; + if (numRepeatTracks > 0) { + for (auto& removedTrack : aTrackInfo->mRemovedRepeatTracks) { + if (removedTrack) { + ++leadingTrackCount; + } + } + } + + for (uint32_t i = aTrackInfo->mStartFragmentTrack; + i < aTrackInfo->mEndFragmentTrack + 1; i++) { + // Since line indexes are 1-based, calculate a 1-based value + // for this track to simplify some calculations. + const uint32_t line1Index = i + 1; + + startOfNextTrack = (i < aTrackInfo->mEndFragmentTrack) + ? aTrackInfo->mPositions[i] + : lastTrackEdge; + + // Get the line names for the current line. aLineInfo->mNames + // may contain duplicate names. This is intentional, since grid + // layout works fine with duplicate names, and we don't want to + // detect and remove duplicates in layout since it is an O(n^2) + // problem. We do the work here since this is only run when + // requested by devtools, and slowness here will not affect + // normal browsing. + nsTArray<RefPtr<nsAtom>> empty{}; + const nsTArray<RefPtr<nsAtom>>& possiblyDuplicateLineNames( + aLineInfo->mNames.SafeElementAt(i, empty)); + + nsTArray<RefPtr<nsAtom>> lineNames; + AddLineNamesIfNotPresent(lineNames, possiblyDuplicateLineNames); + + // Add in names from grid areas where this line is used as a boundary. + for (auto area : aAreas) { + // We specifically ignore line names from implicitly named areas, + // because it can be confusing for designers who might naturally use + // a named line of "-start" or "-end" and create an implicit named + // area without meaning to. + if (area->Type() == GridDeclaration::Implicit) { + continue; + } + + bool haveNameToAdd = false; + nsAutoString nameToAdd; + area->GetName(nameToAdd); + if (aIsRow) { + if (area->RowStart() == line1Index) { + haveNameToAdd = true; + nameToAdd.AppendLiteral("-start"); + } else if (area->RowEnd() == line1Index) { + haveNameToAdd = true; + nameToAdd.AppendLiteral("-end"); + } + } else { + if (area->ColumnStart() == line1Index) { + haveNameToAdd = true; + nameToAdd.AppendLiteral("-start"); + } else if (area->ColumnEnd() == line1Index) { + haveNameToAdd = true; + nameToAdd.AppendLiteral("-end"); + } + } + + if (haveNameToAdd) { + RefPtr<nsAtom> name = NS_Atomize(nameToAdd); + AddLineNameIfNotPresent(lineNames, name); + } + } + + if (i >= (aTrackInfo->mRepeatFirstTrack + + aTrackInfo->mNumLeadingImplicitTracks) && + repeatIndex < numRepeatTracks) { + numAddedLines += AppendRemovedAutoFits( + aTrackInfo, aLineInfo, lastTrackEdge, repeatIndex, numRepeatTracks, + leadingTrackCount, lineNames); + } + + // If this line is the one that ends a repeat, then add + // in the mNamesFollowingRepeat names from aLineInfo. + if (numRepeatTracks > 0 && i == (aTrackInfo->mRepeatFirstTrack + + aTrackInfo->mNumLeadingImplicitTracks + + numRepeatTracks - numAddedLines)) { + AddLineNamesIfNotPresent(lineNames, aLineInfo->mNamesFollowingRepeat); + } + + RefPtr<GridLine> line = new GridLine(this); + mLines.AppendElement(line); + MOZ_ASSERT(line1Index > 0, "line1Index must be positive."); + bool isBeforeFirstExplicit = + (line1Index <= aTrackInfo->mNumLeadingImplicitTracks); + bool isAfterLastExplicit = line1Index > (leadingTrackCount + 1); + // Calculate an actionable line number for this line, that could be used + // in a css grid property to align a grid item or area at that line. + // For implicit lines that appear before line 1, report a number of 0. + // We can't report negative indexes, because those have a different + // meaning in the css grid spec (negative indexes are negative-1-based + // from the end of the grid decreasing towards the front). + uint32_t lineNumber = isBeforeFirstExplicit + ? 0 + : (line1Index + numAddedLines - + aTrackInfo->mNumLeadingImplicitTracks); + + // The negativeNumber is counted back from the leadingTrackCount. + int32_t lineNegativeNumber = + isAfterLastExplicit + ? 0 + : (line1Index + numAddedLines - (leadingTrackCount + 2)); + GridDeclaration lineType = (isBeforeFirstExplicit || isAfterLastExplicit) + ? GridDeclaration::Implicit + : GridDeclaration::Explicit; + line->SetLineValues( + lineNames, nsPresContext::AppUnitsToDoubleCSSPixels(lastTrackEdge), + nsPresContext::AppUnitsToDoubleCSSPixels(startOfNextTrack - + lastTrackEdge), + lineNumber, lineNegativeNumber, lineType); + + if (i < aTrackInfo->mEndFragmentTrack) { + lastTrackEdge = aTrackInfo->mPositions[i] + aTrackInfo->mSizes[i]; + } + } + + // Define a function that gets the mLines index for a given line number. + // This is necessary since it's possible for a line number to not be + // represented in mLines. If this is the case, then return -1. + const int32_t lineCount = mLines.Length(); + const uint32_t lastLineNumber = mLines[lineCount - 1]->Number(); + auto IndexForLineNumber = + [lineCount, lastLineNumber](uint32_t aLineNumber) -> int32_t { + if (lastLineNumber == 0) { + // None of the lines have addressable numbers, so none of them can have + // aLineNumber + return -1; + } + + int32_t possibleIndex = (int32_t)aLineNumber - 1; + if (possibleIndex < 0 || possibleIndex > lineCount - 1) { + // aLineNumber is not represented in mLines. + return -1; + } + + return possibleIndex; + }; + + // Post-processing loop for implicit grid areas. + for (const auto& area : aAreas) { + if (area->Type() == GridDeclaration::Implicit) { + // Get the appropriate indexes for the area's start and end lines as + // they are represented in mLines. + int32_t startIndex = + IndexForLineNumber(aIsRow ? area->RowStart() : area->ColumnStart()); + int32_t endIndex = + IndexForLineNumber(aIsRow ? area->RowEnd() : area->ColumnEnd()); + + // If both start and end indexes are -1, then stop here since we cannot + // reason about the naming for either lines. + if (startIndex < 0 && endIndex < 0) { + break; + } + + // Get the "-start" and "-end" line names of the grid area. + nsAutoString startLineName; + area->GetName(startLineName); + startLineName.AppendLiteral("-start"); + nsAutoString endLineName; + area->GetName(endLineName); + endLineName.AppendLiteral("-end"); + + // Get the list of existing line names for the start and end of the grid + // area. In the case where one of the start or end indexes are -1, use a + // dummy line as a substitute for the start/end line. + RefPtr<GridLine> dummyLine = new GridLine(this); + RefPtr<GridLine> areaStartLine = + startIndex > -1 ? mLines[startIndex] : dummyLine; + nsTArray<RefPtr<nsAtom>> startLineNames(areaStartLine->Names().Clone()); + + RefPtr<GridLine> areaEndLine = + endIndex > -1 ? mLines[endIndex] : dummyLine; + nsTArray<RefPtr<nsAtom>> endLineNames(areaEndLine->Names().Clone()); + + RefPtr<nsAtom> start = NS_Atomize(startLineName); + RefPtr<nsAtom> end = NS_Atomize(endLineName); + if (startLineNames.Contains(end) || endLineNames.Contains(start)) { + // Add the reversed line names. + AddLineNameIfNotPresent(startLineNames, end); + AddLineNameIfNotPresent(endLineNames, start); + } else { + // Add the normal line names. + AddLineNameIfNotPresent(startLineNames, start); + AddLineNameIfNotPresent(endLineNames, end); + } + + areaStartLine->SetLineNames(startLineNames); + areaEndLine->SetLineNames(endLineNames); + } + } + } +} + +uint32_t GridLines::AppendRemovedAutoFits( + const ComputedGridTrackInfo* aTrackInfo, + const ComputedGridLineInfo* aLineInfo, nscoord aLastTrackEdge, + uint32_t& aRepeatIndex, uint32_t aNumRepeatTracks, + uint32_t aNumLeadingTracks, nsTArray<RefPtr<nsAtom>>& aLineNames) { + bool extractedExplicitLineNames = false; + nsTArray<RefPtr<nsAtom>> explicitLineNames; + uint32_t linesAdded = 0; + while (aRepeatIndex < aNumRepeatTracks && + aTrackInfo->mRemovedRepeatTracks[aRepeatIndex]) { + // If this is not the very first call to this function, and if we + // haven't already added a line this call, pull all the explicit + // names to pass along to the next line that will be added after + // this function completes. + if (aRepeatIndex > 0 && linesAdded == 0) { + // Find the names that didn't match the before or after names, + // and extract them. + for (const auto& name : aLineNames) { + if (!aLineInfo->mNamesBefore.Contains(name) && + !aLineInfo->mNamesAfter.Contains(name)) { + explicitLineNames.AppendElement(name); + } + } + for (const auto& extractedName : explicitLineNames) { + aLineNames.RemoveElement(extractedName); + } + extractedExplicitLineNames = true; + } + + AddLineNamesIfNotPresent(aLineNames, aLineInfo->mNamesBefore); + + RefPtr<GridLine> line = new GridLine(this); + mLines.AppendElement(line); + + // Time to calculate the line numbers. For the positive numbers + // we count with a 1-based index from mRepeatFirstTrack. Although + // this number is the index of the first repeat track AFTER all + // the leading implicit tracks, that's still what we want since + // all those leading implicit tracks have line number 0. + uint32_t lineNumber = aTrackInfo->mRepeatFirstTrack + aRepeatIndex + 1; + + // The negative number does have to account for the leading + // implicit tracks. We've been passed aNumLeadingTracks which is + // the total of the leading implicit tracks plus the explicit + // tracks. So all we have to do is subtract that number plus one + // from the 0-based index of this track. + int32_t lineNegativeNumber = + (aTrackInfo->mNumLeadingImplicitTracks + aTrackInfo->mRepeatFirstTrack + + aRepeatIndex) - + (aNumLeadingTracks + 1); + line->SetLineValues( + aLineNames, nsPresContext::AppUnitsToDoubleCSSPixels(aLastTrackEdge), + nsPresContext::AppUnitsToDoubleCSSPixels(0), lineNumber, + lineNegativeNumber, GridDeclaration::Explicit); + + // No matter what, the next line should have the after names associated + // with it. If we go through the loop again, the before names will also + // be added. + aLineNames = aLineInfo->mNamesAfter.Clone(); + aRepeatIndex++; + + linesAdded++; + } + + aRepeatIndex++; + + if (extractedExplicitLineNames) { + // Pass on the explicit names we saved to the next explicit line. + AddLineNamesIfNotPresent(aLineNames, explicitLineNames); + } + + // If we haven't finished adding auto-repeated tracks, then we need to put + // back the before names, in case we cleared them above. + if (aRepeatIndex < aNumRepeatTracks) { + AddLineNamesIfNotPresent(aLineNames, aLineInfo->mNamesBefore); + } + + return linesAdded; +} + +} // namespace mozilla::dom |