mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
f27c852495
Differential Revision: https://phabricator.services.mozilla.com/D176594
373 lines
14 KiB
C++
373 lines
14 KiB
C++
/* -*- 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
|