diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /dom/grid | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/grid')
26 files changed, 3604 insertions, 0 deletions
diff --git a/dom/grid/Grid.cpp b/dom/grid/Grid.cpp new file mode 100644 index 0000000000..fd8eb0817a --- /dev/null +++ b/dom/grid/Grid.cpp @@ -0,0 +1,97 @@ +/* -*- 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 "Grid.h" + +#include "GridArea.h" +#include "GridDimension.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/GridBinding.h" +#include "nsGridContainerFrame.h" +#include "nsTHashSet.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Grid, mParent, mRows, mCols, mAreas) +NS_IMPL_CYCLE_COLLECTING_ADDREF(Grid) +NS_IMPL_CYCLE_COLLECTING_RELEASE(Grid) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Grid) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +Grid::Grid(nsISupports* aParent, nsGridContainerFrame* aFrame) + : mParent(do_QueryInterface(aParent)), + mRows(new GridDimension(this)), + mCols(new GridDimension(this)) { + MOZ_ASSERT(aFrame, + "Should never be instantiated with a null nsGridContainerFrame"); + + // Construct areas first, because lines may need to reference them + // to extract additional names for boundary lines. + + // Add implicit areas first. Track the names that we add here, because + // we will ignore future explicit areas with the same name. + nsTHashSet<RefPtr<nsAtom>> namesSeen; + nsGridContainerFrame::ImplicitNamedAreas* implicitAreas = + aFrame->GetImplicitNamedAreas(); + if (implicitAreas) { + for (auto iter = implicitAreas->iter(); !iter.done(); iter.next()) { + auto& areaInfo = iter.get().value(); + namesSeen.Insert(areaInfo.name.AsAtom()); + GridArea* area = + new GridArea(this, areaInfo.name.AsAtom(), GridDeclaration::Implicit, + areaInfo.rows.start, areaInfo.rows.end, + areaInfo.columns.start, areaInfo.columns.end); + mAreas.AppendElement(area); + } + } + + // Add explicit areas next, as long as they don't have the same name + // as the implicit areas, because the implicit values override what was + // initially available in the explicit areas. + if (auto* explicitAreas = aFrame->GetExplicitNamedAreas()) { + for (auto& areaInfo : explicitAreas->AsSpan()) { + if (!namesSeen.Contains(areaInfo.name.AsAtom())) { + GridArea* area = new GridArea( + this, areaInfo.name.AsAtom(), GridDeclaration::Explicit, + areaInfo.rows.start, areaInfo.rows.end, areaInfo.columns.start, + areaInfo.columns.end); + mAreas.AppendElement(area); + } + } + } + + // Now construct the tracks and lines. + const ComputedGridTrackInfo* rowTrackInfo = aFrame->GetComputedTemplateRows(); + const ComputedGridLineInfo* rowLineInfo = + aFrame->GetComputedTemplateRowLines(); + mRows->SetTrackInfo(rowTrackInfo); + mRows->SetLineInfo(rowTrackInfo, rowLineInfo, mAreas, true); + + const ComputedGridTrackInfo* columnTrackInfo = + aFrame->GetComputedTemplateColumns(); + const ComputedGridLineInfo* columnLineInfo = + aFrame->GetComputedTemplateColumnLines(); + mCols->SetTrackInfo(columnTrackInfo); + mCols->SetLineInfo(columnTrackInfo, columnLineInfo, mAreas, false); +} + +Grid::~Grid() = default; + +JSObject* Grid::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { + return Grid_Binding::Wrap(aCx, this, aGivenProto); +} + +GridDimension* Grid::Rows() const { return mRows; } + +GridDimension* Grid::Cols() const { return mCols; } + +void Grid::GetAreas(nsTArray<RefPtr<GridArea>>& aAreas) const { + aAreas = mAreas.Clone(); +} + +} // namespace mozilla::dom diff --git a/dom/grid/Grid.h b/dom/grid/Grid.h new file mode 100644 index 0000000000..57f13e4044 --- /dev/null +++ b/dom/grid/Grid.h @@ -0,0 +1,48 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_Grid_h +#define mozilla_dom_Grid_h + +#include "GridArea.h" +#include "nsGridContainerFrame.h" +#include "nsISupports.h" +#include "nsWrapperCache.h" + +namespace mozilla::dom { + +class Element; +class GridDimension; + +class Grid : public nsISupports, public nsWrapperCache { + public: + explicit Grid(nsISupports* aParent, nsGridContainerFrame* aFrame); + + protected: + virtual ~Grid(); + + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Grid) + + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + Element* GetParentObject() { return mParent; } + + GridDimension* Rows() const; + GridDimension* Cols() const; + void GetAreas(nsTArray<RefPtr<GridArea>>& aAreas) const; + + protected: + nsCOMPtr<Element> mParent; + RefPtr<GridDimension> mRows; + RefPtr<GridDimension> mCols; + nsTArray<RefPtr<GridArea>> mAreas; +}; + +} // namespace mozilla::dom + +#endif /* mozilla_dom_Grid_h */ diff --git a/dom/grid/GridArea.cpp b/dom/grid/GridArea.cpp new file mode 100644 index 0000000000..fb87bd5b2d --- /dev/null +++ b/dom/grid/GridArea.cpp @@ -0,0 +1,51 @@ +/* -*- 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 "GridArea.h" +#include "mozilla/dom/GridBinding.h" +#include "Grid.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridArea, mParent) +NS_IMPL_CYCLE_COLLECTING_ADDREF(GridArea) +NS_IMPL_CYCLE_COLLECTING_RELEASE(GridArea) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridArea) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +GridArea::GridArea(Grid* aParent, nsAtom* aName, GridDeclaration aType, + uint32_t aRowStart, uint32_t aRowEnd, uint32_t aColumnStart, + uint32_t aColumnEnd) + : mParent(aParent), + mName(aName), + mType(aType), + mRowStart(aRowStart), + mRowEnd(aRowEnd), + mColumnStart(aColumnStart), + mColumnEnd(aColumnEnd) {} + +GridArea::~GridArea() = default; + +JSObject* GridArea::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return GridArea_Binding::Wrap(aCx, this, aGivenProto); +} + +void GridArea::GetName(nsString& aName) const { mName->ToString(aName); } + +GridDeclaration GridArea::Type() const { return mType; } + +uint32_t GridArea::RowStart() const { return mRowStart; } + +uint32_t GridArea::RowEnd() const { return mRowEnd; } + +uint32_t GridArea::ColumnStart() const { return mColumnStart; } + +uint32_t GridArea::ColumnEnd() const { return mColumnEnd; } + +} // namespace mozilla::dom diff --git a/dom/grid/GridArea.h b/dom/grid/GridArea.h new file mode 100644 index 0000000000..64734f7d91 --- /dev/null +++ b/dom/grid/GridArea.h @@ -0,0 +1,55 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_GridArea_h +#define mozilla_dom_GridArea_h + +#include "mozilla/dom/GridBinding.h" +#include "nsWrapperCache.h" + +class nsAtom; + +namespace mozilla::dom { + +class Grid; + +class GridArea : public nsISupports, public nsWrapperCache { + public: + explicit GridArea(Grid* aParent, nsAtom* aName, GridDeclaration aType, + uint32_t aRowStart, uint32_t aRowEnd, uint32_t aColumnStart, + uint32_t aColumnEnd); + + protected: + virtual ~GridArea(); + + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(GridArea) + + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + Grid* GetParentObject() { return mParent; } + + void GetName(nsString& aName) const; + GridDeclaration Type() const; + uint32_t RowStart() const; + uint32_t RowEnd() const; + uint32_t ColumnStart() const; + uint32_t ColumnEnd() const; + + protected: + RefPtr<Grid> mParent; + const RefPtr<nsAtom> mName; + const GridDeclaration mType; + const uint32_t mRowStart; + const uint32_t mRowEnd; + const uint32_t mColumnStart; + const uint32_t mColumnEnd; +}; + +} // namespace mozilla::dom + +#endif /* mozilla_dom_GridTrack_h */ diff --git a/dom/grid/GridDimension.cpp b/dom/grid/GridDimension.cpp new file mode 100644 index 0000000000..f7dd997283 --- /dev/null +++ b/dom/grid/GridDimension.cpp @@ -0,0 +1,54 @@ +/* -*- 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 "GridDimension.h" + +#include "Grid.h" +#include "GridLines.h" +#include "GridTracks.h" +#include "mozilla/dom/GridBinding.h" +#include "nsGridContainerFrame.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridDimension, mParent, mLines, mTracks) +NS_IMPL_CYCLE_COLLECTING_ADDREF(GridDimension) +NS_IMPL_CYCLE_COLLECTING_RELEASE(GridDimension) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridDimension) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +GridDimension::GridDimension(Grid* aParent) + : mParent(aParent), + mLines(new GridLines(this)), + mTracks(new GridTracks(this)) { + MOZ_ASSERT(aParent, "Should never be instantiated with a null Grid"); +} + +GridDimension::~GridDimension() = default; + +JSObject* GridDimension::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return GridDimension_Binding::Wrap(aCx, this, aGivenProto); +} + +GridLines* GridDimension::Lines() const { return mLines; } + +GridTracks* GridDimension::Tracks() const { return mTracks; } + +void GridDimension::SetTrackInfo(const ComputedGridTrackInfo* aTrackInfo) { + mTracks->SetTrackInfo(aTrackInfo); +} + +void GridDimension::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo, + const ComputedGridLineInfo* aLineInfo, + const nsTArray<RefPtr<GridArea>>& aAreas, + bool aIsRow) { + mLines->SetLineInfo(aTrackInfo, aLineInfo, aAreas, aIsRow); +} + +} // namespace mozilla::dom diff --git a/dom/grid/GridDimension.h b/dom/grid/GridDimension.h new file mode 100644 index 0000000000..9d3b6174c7 --- /dev/null +++ b/dom/grid/GridDimension.h @@ -0,0 +1,57 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_GridDimension_h +#define mozilla_dom_GridDimension_h + +#include "nsWrapperCache.h" +#include "nsTArray.h" + +namespace mozilla { + +struct ComputedGridLineInfo; +struct ComputedGridTrackInfo; + +namespace dom { + +class Grid; +class GridArea; +class GridLines; +class GridTracks; + +class GridDimension : public nsISupports, public nsWrapperCache { + public: + explicit GridDimension(Grid* aParent); + + protected: + virtual ~GridDimension(); + + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(GridDimension) + + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + Grid* GetParentObject() { return mParent; } + + GridLines* Lines() const; + GridTracks* Tracks() const; + + void SetTrackInfo(const ComputedGridTrackInfo* aTrackInfo); + void SetLineInfo(const ComputedGridTrackInfo* aTrackInfo, + const ComputedGridLineInfo* aLineInfo, + const nsTArray<RefPtr<GridArea>>& aAreas, bool aIsRow); + + protected: + RefPtr<Grid> mParent; + RefPtr<GridLines> mLines; + RefPtr<GridTracks> mTracks; +}; + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_GridDimension_h */ diff --git a/dom/grid/GridLine.cpp b/dom/grid/GridLine.cpp new file mode 100644 index 0000000000..ce65b5bbd8 --- /dev/null +++ b/dom/grid/GridLine.cpp @@ -0,0 +1,72 @@ +/* -*- 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 "GridLine.h" + +#include "GridLines.h" +#include "mozilla/dom/GridBinding.h" +#include "nsAtom.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridLine, mParent) +NS_IMPL_CYCLE_COLLECTING_ADDREF(GridLine) +NS_IMPL_CYCLE_COLLECTING_RELEASE(GridLine) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridLine) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +GridLine::GridLine(GridLines* aParent) + : mParent(aParent), + mStart(0.0), + mBreadth(0.0), + mType(GridDeclaration::Implicit), + mNumber(0), + mNegativeNumber(0) { + MOZ_ASSERT(aParent, "Should never be instantiated with a null GridLines"); +} + +GridLine::~GridLine() = default; + +void GridLine::GetNames(nsTArray<nsString>& aNames) const { + aNames.SetCapacity(mNames.Length()); + for (auto& name : mNames) { + aNames.AppendElement(nsDependentAtomString(name)); + } +} + +JSObject* GridLine::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return GridLine_Binding::Wrap(aCx, this, aGivenProto); +} + +double GridLine::Start() const { return mStart; } + +double GridLine::Breadth() const { return mBreadth; } + +GridDeclaration GridLine::Type() const { return mType; } + +uint32_t GridLine::Number() const { return mNumber; } + +int32_t GridLine::NegativeNumber() const { return mNegativeNumber; } + +void GridLine::SetLineValues(const nsTArray<RefPtr<nsAtom>>& aNames, + double aStart, double aBreadth, uint32_t aNumber, + int32_t aNegativeNumber, GridDeclaration aType) { + mNames = aNames.Clone(); + mStart = aStart; + mBreadth = aBreadth; + mNumber = aNumber; + mNegativeNumber = aNegativeNumber; + mType = aType; +} + +void GridLine::SetLineNames(const nsTArray<RefPtr<nsAtom>>& aNames) { + mNames = aNames.Clone(); +} + +} // namespace mozilla::dom diff --git a/dom/grid/GridLine.h b/dom/grid/GridLine.h new file mode 100644 index 0000000000..bcc3325583 --- /dev/null +++ b/dom/grid/GridLine.h @@ -0,0 +1,63 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_GridLine_h +#define mozilla_dom_GridLine_h + +#include "mozilla/dom/GridBinding.h" +#include "nsString.h" +#include "nsTArray.h" +#include "nsWrapperCache.h" + +class nsAtom; + +namespace mozilla::dom { + +class GridLines; + +class GridLine : public nsISupports, public nsWrapperCache { + public: + explicit GridLine(GridLines* aParent); + + protected: + virtual ~GridLine(); + + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(GridLine) + + void GetNames(nsTArray<nsString>& aNames) const; + const nsTArray<RefPtr<nsAtom>>& Names() const { return mNames; } + + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + GridLines* GetParentObject() { return mParent; } + + double Start() const; + double Breadth() const; + GridDeclaration Type() const; + uint32_t Number() const; + int32_t NegativeNumber() const; + + void SetLineValues(const nsTArray<RefPtr<nsAtom>>& aNames, double aStart, + double aBreadth, uint32_t aNumber, int32_t aNegativeNumber, + GridDeclaration aType); + + void SetLineNames(const nsTArray<RefPtr<nsAtom>>& aNames); + + protected: + RefPtr<GridLines> mParent; + nsTArray<RefPtr<nsAtom>> mNames; + double mStart; + double mBreadth; + GridDeclaration mType; + uint32_t mNumber; + int32_t mNegativeNumber; +}; + +} // namespace mozilla::dom + +#endif /* mozilla_dom_GridLine_h */ diff --git a/dom/grid/GridLines.cpp b/dom/grid/GridLines.cpp new file mode 100644 index 0000000000..ccb6339daf --- /dev/null +++ b/dom/grid/GridLines.cpp @@ -0,0 +1,371 @@ +/* -*- 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. + const nsTArray<RefPtr<nsAtom>>& possiblyDuplicateLineNames( + aLineInfo->mNames.SafeElementAt(i, nsTArray<RefPtr<nsAtom>>())); + + 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 diff --git a/dom/grid/GridLines.h b/dom/grid/GridLines.h new file mode 100644 index 0000000000..f95993dc6d --- /dev/null +++ b/dom/grid/GridLines.h @@ -0,0 +1,65 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_GridLines_h +#define mozilla_dom_GridLines_h + +#include "nsCoord.h" +#include "nsTArray.h" +#include "nsWrapperCache.h" + +class nsAtom; + +namespace mozilla { + +struct ComputedGridTrackInfo; +struct ComputedGridLineInfo; + +namespace dom { + +class GridDimension; +class GridLine; +class GridArea; + +class GridLines : public nsISupports, public nsWrapperCache { + public: + explicit GridLines(GridDimension* aParent); + + protected: + virtual ~GridLines(); + + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(GridLines) + + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + GridDimension* GetParentObject() { return mParent; } + + uint32_t Length() const; + GridLine* Item(uint32_t aIndex); + GridLine* IndexedGetter(uint32_t aIndex, bool& aFound); + + void SetLineInfo(const ComputedGridTrackInfo* aTrackInfo, + const ComputedGridLineInfo* aLineInfo, + const nsTArray<RefPtr<GridArea>>& aAreas, bool aIsRow); + + protected: + uint32_t AppendRemovedAutoFits(const ComputedGridTrackInfo* aTrackInfo, + const ComputedGridLineInfo* aLineInfo, + nscoord aLastTrackEdge, uint32_t& aRepeatIndex, + uint32_t aNumRepeatTracks, + uint32_t aNumLeadingTracks, + nsTArray<RefPtr<nsAtom>>& aLineNames); + + RefPtr<GridDimension> mParent; + nsTArray<RefPtr<GridLine>> mLines; +}; + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_GridLines_h */ diff --git a/dom/grid/GridTrack.cpp b/dom/grid/GridTrack.cpp new file mode 100644 index 0000000000..b2d9d0b3b3 --- /dev/null +++ b/dom/grid/GridTrack.cpp @@ -0,0 +1,54 @@ +/* -*- 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 "GridTrack.h" + +#include "GridTracks.h" +#include "mozilla/dom/GridBinding.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridTrack, mParent) +NS_IMPL_CYCLE_COLLECTING_ADDREF(GridTrack) +NS_IMPL_CYCLE_COLLECTING_RELEASE(GridTrack) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridTrack) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +GridTrack::GridTrack(GridTracks* aParent) + : mParent(aParent), + mStart(0.0), + mBreadth(0.0), + mType(GridDeclaration::Implicit), + mState(GridTrackState::Static) { + MOZ_ASSERT(aParent, "Should never be instantiated with a null GridTracks"); +} + +GridTrack::~GridTrack() = default; + +JSObject* GridTrack::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return GridTrack_Binding::Wrap(aCx, this, aGivenProto); +} + +double GridTrack::Start() const { return mStart; } + +double GridTrack::Breadth() const { return mBreadth; } + +GridDeclaration GridTrack::Type() const { return mType; } + +GridTrackState GridTrack::State() const { return mState; } + +void GridTrack::SetTrackValues(double aStart, double aBreadth, + GridDeclaration aType, GridTrackState aState) { + mStart = aStart; + mBreadth = aBreadth; + mType = aType; + mState = aState; +} + +} // namespace mozilla::dom diff --git a/dom/grid/GridTrack.h b/dom/grid/GridTrack.h new file mode 100644 index 0000000000..fa78c2d5d0 --- /dev/null +++ b/dom/grid/GridTrack.h @@ -0,0 +1,50 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_GridTrack_h +#define mozilla_dom_GridTrack_h + +#include "mozilla/dom/GridBinding.h" +#include "nsWrapperCache.h" + +namespace mozilla::dom { + +class GridTracks; + +class GridTrack : public nsISupports, public nsWrapperCache { + public: + explicit GridTrack(GridTracks* parent); + + protected: + virtual ~GridTrack(); + + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(GridTrack) + + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + GridTracks* GetParentObject() { return mParent; } + + double Start() const; + double Breadth() const; + GridDeclaration Type() const; + GridTrackState State() const; + + void SetTrackValues(double aStart, double aBreadth, GridDeclaration aType, + GridTrackState aState); + + protected: + RefPtr<GridTracks> mParent; + double mStart; + double mBreadth; + GridDeclaration mType; + GridTrackState mState; +}; + +} // namespace mozilla::dom + +#endif /* mozilla_dom_GridTrack_h */ diff --git a/dom/grid/GridTracks.cpp b/dom/grid/GridTracks.cpp new file mode 100644 index 0000000000..f3f4b98961 --- /dev/null +++ b/dom/grid/GridTracks.cpp @@ -0,0 +1,108 @@ +/* -*- 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 "GridTracks.h" + +#include "GridDimension.h" +#include "GridTrack.h" +#include "mozilla/dom/GridBinding.h" +#include "nsGridContainerFrame.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridTracks, mParent, mTracks) +NS_IMPL_CYCLE_COLLECTING_ADDREF(GridTracks) +NS_IMPL_CYCLE_COLLECTING_RELEASE(GridTracks) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridTracks) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +GridTracks::GridTracks(GridDimension* aParent) : mParent(aParent) { + MOZ_ASSERT(aParent, "Should never be instantiated with a null GridDimension"); +} + +GridTracks::~GridTracks() = default; + +JSObject* GridTracks::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return GridTracks_Binding::Wrap(aCx, this, aGivenProto); +} + +uint32_t GridTracks::Length() const { return mTracks.Length(); } + +GridTrack* GridTracks::Item(uint32_t aIndex) { + return mTracks.SafeElementAt(aIndex); +} + +GridTrack* GridTracks::IndexedGetter(uint32_t aIndex, bool& aFound) { + aFound = aIndex < mTracks.Length(); + if (!aFound) { + return nullptr; + } + return mTracks[aIndex]; +} + +void GridTracks::SetTrackInfo(const ComputedGridTrackInfo* aTrackInfo) { + // rebuild the tracks based on aTrackInfo + mTracks.Clear(); + + if (!aTrackInfo) { + return; + } + + nscoord lastTrackEdge = 0; + uint32_t repeatIndex = 0; + auto AppendRemovedAutoFits = [this, &aTrackInfo, &lastTrackEdge, + &repeatIndex]() { + uint32_t numRepeatTracks = aTrackInfo->mRemovedRepeatTracks.Length(); + // Add in removed auto-fit tracks + while (repeatIndex < numRepeatTracks && + aTrackInfo->mRemovedRepeatTracks[repeatIndex]) { + RefPtr<GridTrack> track = new GridTrack(this); + mTracks.AppendElement(track); + track->SetTrackValues( + nsPresContext::AppUnitsToDoubleCSSPixels(lastTrackEdge), + nsPresContext::AppUnitsToDoubleCSSPixels(0), + GridDeclaration::Explicit, GridTrackState::Removed); + repeatIndex++; + } + repeatIndex++; + }; + + for (size_t i = aTrackInfo->mStartFragmentTrack; + i < aTrackInfo->mEndFragmentTrack; i++) { + if (i >= aTrackInfo->mRepeatFirstTrack) { + // Append removed auto-fit tracks, if appropriate. The + // AppendRemovedAutoFits function exits early once it has been called + // aTrackInfo->mRemovedRepeatTracks.Length() times -- a check we don't + // replicate here or at subsequent calling sites. + AppendRemovedAutoFits(); + } + + RefPtr<GridTrack> track = new GridTrack(this); + mTracks.AppendElement(track); + track->SetTrackValues( + nsPresContext::AppUnitsToDoubleCSSPixels(aTrackInfo->mPositions[i]), + nsPresContext::AppUnitsToDoubleCSSPixels(aTrackInfo->mSizes[i]), + ( + // Implicit if index is before the first explicit track, or after + // the last explicit track. + (i < aTrackInfo->mNumLeadingImplicitTracks) || + (i >= aTrackInfo->mNumLeadingImplicitTracks + + aTrackInfo->mNumExplicitTracks) + ? GridDeclaration::Implicit + : GridDeclaration::Explicit), + GridTrackState(aTrackInfo->mStates[i])); + + lastTrackEdge = aTrackInfo->mPositions[i] + aTrackInfo->mSizes[i]; + } + + // Append any trailing removed auto-fit tracks. + AppendRemovedAutoFits(); +} + +} // namespace mozilla::dom diff --git a/dom/grid/GridTracks.h b/dom/grid/GridTracks.h new file mode 100644 index 0000000000..d20da5e12b --- /dev/null +++ b/dom/grid/GridTracks.h @@ -0,0 +1,51 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_GridTracks_h +#define mozilla_dom_GridTracks_h + +#include "nsTArray.h" +#include "nsWrapperCache.h" + +namespace mozilla { + +struct ComputedGridTrackInfo; + +namespace dom { + +class GridDimension; +class GridTrack; + +class GridTracks : public nsISupports, public nsWrapperCache { + public: + explicit GridTracks(GridDimension* aParent); + + protected: + virtual ~GridTracks(); + + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(GridTracks) + + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + GridDimension* GetParentObject() { return mParent; } + + uint32_t Length() const; + GridTrack* Item(uint32_t aIndex); + GridTrack* IndexedGetter(uint32_t aIndex, bool& aFound); + + void SetTrackInfo(const mozilla::ComputedGridTrackInfo* aTrackInfo); + + protected: + RefPtr<GridDimension> mParent; + nsTArray<RefPtr<GridTrack>> mTracks; +}; + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_GridTracks_h */ diff --git a/dom/grid/moz.build b/dom/grid/moz.build new file mode 100644 index 0000000000..64d54fd8c6 --- /dev/null +++ b/dom/grid/moz.build @@ -0,0 +1,36 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +with Files("**"): + BUG_COMPONENT = ("Core", "CSS Parsing and Computation") + +MOCHITEST_CHROME_MANIFESTS += ["test/chrome.ini"] + +EXPORTS.mozilla.dom += [ + "Grid.h", + "GridArea.h", + "GridDimension.h", + "GridLine.h", + "GridLines.h", + "GridTrack.h", + "GridTracks.h", +] + +UNIFIED_SOURCES += [ + "Grid.cpp", + "GridArea.cpp", + "GridDimension.cpp", + "GridLine.cpp", + "GridLines.cpp", + "GridTrack.cpp", + "GridTracks.cpp", +] + +LOCAL_INCLUDES += [ + "/layout/generic", +] + +FINAL_LIBRARY = "xul" diff --git a/dom/grid/test/chrome.ini b/dom/grid/test/chrome.ini new file mode 100644 index 0000000000..d2ef64e657 --- /dev/null +++ b/dom/grid/test/chrome.ini @@ -0,0 +1,10 @@ +[chrome/test_grid_areas.html] +[chrome/test_grid_fragmentation.html] +[chrome/test_grid_implicit.html] +[chrome/test_grid_lines.html] +[chrome/test_grid_line_numbers.html] +[chrome/test_grid_object.html] +[chrome/test_grid_repeat_auto_fit.html] +[chrome/test_grid_repeat_auto_fill.html] +[chrome/test_grid_tracks.html] +[chrome/test_grid_subtree_dirty.html] diff --git a/dom/grid/test/chrome/test_grid_areas.html b/dom/grid/test/chrome/test_grid_areas.html new file mode 100644 index 0000000000..22c7cc53dd --- /dev/null +++ b/dom/grid/test/chrome/test_grid_areas.html @@ -0,0 +1,143 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +<style> +body { + margin: 40px; +} +.wrapper { + display: grid; + width: 600px; + height: 600px; + grid-gap: 20px; + grid-template-columns: 50px 1fr 50px; + grid-template-rows: 1fr 1fr 1fr; + grid-template-areas: "areaA areaA ....." + "areaB areaC areaC" + "areaB areaC areaC"; + background-color: #f00; +} +.box { + background-color: #444; + color: #fff; +} +.a { + grid-area: areaA; +} +.b { + grid-area: areaB; +} +.c { + grid-area: areaC; +} +</style> + +<script> +"use strict"; + +SimpleTest.waitForExplicitFinish(); + +function runTests() { + var wrapper = document.getElementById("wrapper"); + var grid = wrapper.getGridFragments()[0]; + + // test existence of property + isnot(typeof(grid.areas), "undefined", "Grid.areas property exists."); + + if (typeof(grid.areas) != "undefined") { + // test that the right number of areas are reported + is(grid.areas.length, 3, "3 areas are reported."); + + if (grid.areas.length == 3) { + // test area names + is(grid.areas[0].name, "areaA", "Area 0 has proper name."); + is(grid.areas[1].name, "areaB", "Area 1 has proper name."); + is(grid.areas[2].name, "areaC", "Area 2 has proper name."); + + // test area types + is(grid.areas[0].type, "explicit", "Area 0 is explicit."); + is(grid.areas[1].type, "explicit", "Area 1 is explicit."); + is(grid.areas[2].type, "explicit", "Area 2 is explicit."); + + // test numbers of start and end lines + is(grid.areas[0].rowStart, 1, "Area 0 has start row line of 1."); + is(grid.areas[0].rowEnd, 2, "Area 0 has end row line of 2."); + is(grid.areas[0].columnStart, 1, "Area 0 has start column line of 1."); + is(grid.areas[0].columnEnd, 3, "Area 0 has end column line of 3."); + + is(grid.areas[1].rowStart, 2, "Area 1 has start row line of 2."); + is(grid.areas[1].rowEnd, 4, "Area 1 has end row line of 4."); + is(grid.areas[1].columnStart, 1, "Area 1 has start column line of 1."); + is(grid.areas[1].columnEnd, 2, "Area 1 has end column line of 2."); + + is(grid.areas[2].rowStart, 2, "Area 2 has start row line of 2."); + is(grid.areas[2].rowEnd, 4, "Area 2 has end row line of 4."); + is(grid.areas[2].columnStart, 2, "Area 2 has start column line of 2."); + is(grid.areas[2].columnEnd, 4, "Area 2 has end column line of 4."); + + // test names of all the row lines + isnot(grid.rows.lines[0].names.indexOf("areaA-start"), -1, + "Grid row line 1 has the name 'areaA-start'." + ); + + isnot(grid.rows.lines[1].names.indexOf("areaA-end"), -1, + "Grid row line 2 has the name 'areaA-end'." + ); + isnot(grid.rows.lines[1].names.indexOf("areaB-start"), -1, + "Grid row line 2 has the name 'areaB-start'." + ); + isnot(grid.rows.lines[1].names.indexOf("areaC-start"), -1, + "Grid row line 2 has the name 'areaC-start'." + ); + + is(grid.rows.lines[2].names.length, 0, "Grid row line 3 has no names."); + + isnot(grid.rows.lines[3].names.indexOf("areaB-end"), -1, + "Grid row line 4 has the name 'areaB-end'." + ); + isnot(grid.rows.lines[3].names.indexOf("areaC-end"), -1, + "Grid row line 4 has the name 'areaC-end'." + ); + + // test names of all the column lines + isnot(grid.cols.lines[0].names.indexOf("areaA-start"), -1, + "Grid column line 1 has the name 'areaA-start'." + ); + isnot(grid.cols.lines[0].names.indexOf("areaB-start"), -1, + "Grid column line 1 has the name 'areaB-start'." + ); + + isnot(grid.cols.lines[1].names.indexOf("areaB-end"), -1, + "Grid column line 2 has the name 'areaB-end'." + ); + isnot(grid.cols.lines[1].names.indexOf("areaC-start"), -1, + "Grid column line 2 has the name 'areaC-start'." + ); + + isnot(grid.cols.lines[2].names.indexOf("areaA-end"), -1, + "Grid column line 3 has the name 'areaA-end'." + ); + + isnot(grid.cols.lines[3].names.indexOf("areaC-end"), -1, + "Grid column line 4 has the name 'areaC-end'." + ); + } + } + + SimpleTest.finish(); +} +</script> +</head> +<body onLoad="runTests();"> + + <div id="wrapper" class="wrapper"> + <div id="boxA" class="box a">A</div> + <div id="boxB" class="box b">B</div> + <div id="boxC" class="box c">C</div> + </div> + +</body> +</html> diff --git a/dom/grid/test/chrome/test_grid_fragmentation.html b/dom/grid/test/chrome/test_grid_fragmentation.html new file mode 100644 index 0000000000..f9a28ee1a9 --- /dev/null +++ b/dom/grid/test/chrome/test_grid_fragmentation.html @@ -0,0 +1,86 @@ +<!DOCTYPE HTML> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html><head> + <meta charset="utf-8"> + <title>CSS Grid Test: Fragmentation of height:auto grid, not top-of-page</title> + <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1144096"> + <link rel="help" href="https://drafts.csswg.org/css-grid/#pagination"> + <link rel="match" href="grid-fragmentation-001-ref.html"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <style type="text/css"> +html,body { + color:black; background-color:white; font-size:16px; padding:0; margin:0; +} +body { overflow:hidden; } + +.columns { + position:relative; + -ms-columns: 5; + -webkit-columns: 5; + columns: 5; + -ms-column-fill: auto; + -webkit-column-fill: auto; + column-fill: auto; + border: 2px dashed; + margin-bottom: 5px; +} + +.grid { + display: grid; + grid-template-columns: 30px 30px 30px; + grid-auto-rows: 50px; + grid-gap: 12px; + border:5px solid; + align-content: start; +} +span { background:lime; border:1px solid black; } +x { display:block; height:20px; } + +</style> + +<script> +"use strict"; + +SimpleTest.waitForExplicitFinish(); + +function runTests() { + var wrapper = document.getElementById("wrapper"); + var fragments = wrapper.getGridFragments(); + + // test fragments of the grid + is(fragments.length, 2, "Grid is split into two fragments."); + + if (fragments.length == 2) { + var grid0 = fragments[0]; + var grid1 = fragments[1]; + + // test that both fragments have one row track and two lines + is(grid0.rows.tracks.length, 1, "Fragment 0 has one row track."); + is(grid0.rows.lines.length, 2, "Fragment 0 has two row lines."); + is(grid1.rows.tracks.length, 1, "Fragment 1 has one row track."); + is(grid1.rows.lines.length, 2, "Fragment 1 has two row lines."); + } + + SimpleTest.finish(); +} +</script> +</head> +<body onLoad="runTests();"> + +<div class="columns" style="height: 100px/*fragmentainer ends in the last row*/"> +<div style="padding-top:10px; background:grey"> +<div id="wrapper" class="grid"> +<span style="grid-row:span 2"><x></x></span> +<span style="height:60px; background:cyan"><x></x></span> +<span style="align-self:end; background:pink"><x></x></span> +<span style="grid-row:1; height:60px"><x></x></span> +</div></div></div> + +</body> +</html> diff --git a/dom/grid/test/chrome/test_grid_implicit.html b/dom/grid/test/chrome/test_grid_implicit.html new file mode 100644 index 0000000000..82f3284dad --- /dev/null +++ b/dom/grid/test/chrome/test_grid_implicit.html @@ -0,0 +1,390 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +<style> +body { + margin: 40px; +} +.wrapper { + display: grid; + grid-gap: 10px; + grid-auto-columns: 20px; + grid-auto-rows: 20px; + background-color: #f00; +} + +.template1 { + grid-template-columns: 100px 50px 100px; + grid-template-rows: 50px [areaD-start middle] 50px [areaD-end]; + grid-template-areas: "areaA areaA ....." + "..... areaC areaC"; +} + +.template2 { + grid-template-areas: "..... areaA ......"; + grid-template-columns: [areaA-start] 50px 50px 50px; +} + +.template3 { + grid-template-columns: [areaA-start areaB-end areaC-end areaD-start] 50px [areaA-end areaB-start areaC-start areaD-end]; + grid-template-rows: [areaA-end areaB-start areaC-end] 50px [areaA-start areaB-end areaC-start]; +} + +.template4 { + grid-template-columns: 100px 50px 100px; + grid-template-rows: 50px; +} + +.template5 { + grid-template-columns: [a-end] 100px [b]; +} + +.template6 { + grid-template-columns: [foo-end] 100px [foo-start]; +} + +.box { + background-color: #444; + color: #fff; +} +.a { + grid-area: areaA; +} +.b { + grid-row: span got-this-name-implicitly / 2; + grid-column: areaA-end / span 2; +} +.c { + grid-area: areaC; +} +.d { + grid-area: areaD; +} +.e { + grid-column: -7 / 5; +} +.f { + grid-column: a-start / a-end; +} +.g { + grid-column: a-start / 2 a-end; +} +.h { + grid-column: foo-start; +} +</style> + +<script> +"use strict"; + +SimpleTest.waitForExplicitFinish(); + +function runTests() { + // test the first grid wrapper + let wrapper = document.getElementById("wrapper1"); + let grid = wrapper.getGridFragments()[0]; + + // test column and row line counts + is(grid.cols.lines.length, 6, + "Grid.cols.lines property has length that respects implicit expansion." + ); + is(grid.rows.lines.length, 4, + "Grid.rows.lines property has length that respects implicit expansion." + ); + + if ((grid.cols.lines.length == 6) && + (grid.rows.lines.length == 4)) { + // test explicit / implicit lines + is(grid.cols.lines[0].type, "explicit", "Grid column line 1 is explicit."); + is(grid.cols.lines[4].type, "implicit", "Grid column line 5 is implicit."); + is(grid.cols.lines[5].type, "implicit", "Grid column line 6 is implicit."); + + + is(grid.rows.lines[0].type, "implicit", "Grid row line 0 is implicit."); + is(grid.rows.lines[0].number, 0, "Grid row line 0 has correct number."); + is(grid.rows.lines[1].type, "explicit", "Grid row line 1 is explicit."); + is(grid.rows.lines[1].number, 1, "Grid row line 1 has correct number."); + is(grid.rows.lines[3].type, "explicit", "Grid row line 3 is explicit."); + is(grid.rows.lines[3].number, 3, "Grid row line 3 has correct number."); + + // test that row line 1 gets the name forced on it by placement of item B + todo_isnot(grid.rows.lines[0].names.indexOf("got-this-name-implicitly"), -1, + "Grid row line 1 has the name 'got-this-name-implicitly'." + ); + + // test that row line 3 gets its explicit name + isnot(grid.rows.lines[2].names.indexOf("middle"), -1, + "Grid row line 3 has the name 'middle'." + ); + + // test the names of the implicit column lines that were created for area 'areaD' + isnot(grid.cols.lines[4].names.indexOf("areaD-start"), -1, + "Grid column line 5 has the name 'areaD-start'." + ); + isnot(grid.cols.lines[5].names.indexOf("areaD-end"), -1, + "Grid column line 6 has the name 'areaD-end'." + ); + } + + // test column and row track counts + is(grid.cols.tracks.length, 5, + "Grid.cols.tracks property has length that respects implicit expansion." + ); + is(grid.rows.tracks.length, 3, + "Grid.rows.tracks property has length that respects implicit expansion." + ); + + if ((grid.cols.tracks.length == 5) && + (grid.rows.tracks.length == 3)) { + // test explicit / implicit tracks + is(grid.cols.tracks[0].type, "explicit", "Grid column track 1 is explicit."); + is(grid.cols.tracks[3].type, "implicit", "Grid column track 4 is implicit."); + is(grid.cols.tracks[4].type, "implicit", "Grid column track 5 is implicit."); + + is(grid.rows.tracks[0].type, "implicit", "Grid row track 1 is implicit."); + is(grid.rows.tracks[1].type, "explicit", "Grid row track 2 is explicit."); + is(grid.rows.tracks[2].type, "explicit", "Grid row track 3 is explicit."); + } + + // test area count + is(grid.areas.length, 3, + "Grid.areas property has length that respects implicit expansion." + ); + + for (let i = 0; i < grid.areas.length; i++) { + let area = grid.areas[i]; + if (area.name == "areaD") { + is(area.type, "implicit", area.name + " is implicit."); + + // test lines of implicit areas + is(area.rowStart, 3, area.name + " has start row line of 3."); + is(area.rowEnd, 4, area.name + " has end row line of 4."); + is(area.columnStart, 5, area.name + " has start column line of 5."); + is(area.columnEnd, 6, area.name + " has end column line of 6."); + } else { + is(area.type, "explicit", area.name + " is explicit."); + } + } + + + // test the second grid wrapper + wrapper = document.getElementById("wrapper2"); + grid = wrapper.getGridFragments()[0]; + + // test column and row line counts + is(grid.cols.lines.length, 4, + "Grid.cols.lines property doesn't expand due to an explicit line declaration." + ); + is(grid.rows.lines.length, 2, + "Grid.rows.lines property has length that respects implicit expansion." + ); + + // test area count + is(grid.areas.length, 1, + "Grid.areas property has length that respects implicit expansion." + ); + + for (let i = 0; i < grid.areas.length; i++) { + let area = grid.areas[i]; + if (area.name == "areaA") { + is(area.type, "implicit", area.name + " is implicit."); + + // test lines of implicit areas + is(area.rowStart, 1, area.name + " has start row line of 1."); + is(area.rowEnd, 2, area.name + " has end row line of 2."); + is(area.columnStart, 1, area.name + " has start column line of 1."); + is(area.columnEnd, 3, area.name + " has end column line of 3."); + } + } + + + // test the third grid wrapper + wrapper = document.getElementById("wrapper3"); + grid = wrapper.getGridFragments()[0]; + + // test column and row line counts + is(grid.cols.lines.length, 2, + "Grid.cols.lines property doesn't expand due to an explicit line declaration." + ); + is(grid.rows.lines.length, 2, + "Grid.rows.lines property doesn't expand due to an explicit line declaration." + ); + + if (grid.cols.lines.length == 2 && grid.rows.lines.length == 2) { + // check that areaC gets only the explicit line names and + // doesn't get the implicit line names + is(grid.cols.lines[0].names.indexOf("areaC-start"), -1, + "Grid row line 1 doesn't have the name 'areaC-start'." + ); + + isnot(grid.cols.lines[0].names.indexOf("areaC-end"), -1, + "Grid row line 1 has the name 'areaC-end'." + ); + + isnot(grid.cols.lines[1].names.indexOf("areaC-start"), -1, + "Grid row line 2 has the name 'areaC-start'." + ); + + is(grid.cols.lines[1].names.indexOf("areaC-end"), -1, + "Grid row line 2 doesn't have the name 'areaC-end'." + ); + } + + // test area count + is(grid.areas.length, 4, + "Grid.areas property reports 4 areas." + ); + + for (let i = 0; i < grid.areas.length; i++) { + let area = grid.areas[i]; + if (area.name == "areaC") { + // test lines of implicit area + is(area.rowStart, 1, area.name + " has start row line of 1."); + is(area.rowEnd, 2, area.name + " has end row line of 2."); + is(area.columnStart, 1, area.name + " has start column line of 1."); + is(area.columnEnd, 2, area.name + " has end column line of 2."); + } + } + + // test the fourth grid wrapper + wrapper = document.getElementById("wrapper4"); + grid = wrapper.getGridFragments()[0]; + + // test column and row line counts + is(grid.cols.lines.length, 8, + "Grid.cols.lines property expands properly with implicit columns on both sides." + ); + is(grid.rows.lines.length, 2, + "Grid.rows.lines property is as expected" + ); + + if (grid.cols.lines.length == 8) { + // check that all the lines get correct implict/explicit type and number + let expectedType = [ + "implicit", + "implicit", + "implicit", + "explicit", + "explicit", + "explicit", + "explicit", + "implicit", + ]; + let expectedNumber = [ + 0, + 0, + 0, + 1, + 2, + 3, + 4, + 5, + ]; + let expectedNegativeNumber = [ + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 0, + ]; + + for (let i = 0; i < grid.cols.lines.length; i++) { + let line = grid.cols.lines[i]; + is(line.type, expectedType[i], "Line index " + i + " has expected type."); + is(line.number, expectedNumber[i], "Line index " + i + " has expected number."); + is(line.negativeNumber, expectedNegativeNumber[i], "Line index " + i + " has expected negative number."); + } + } + + // Test the fifth grid wrapper + wrapper = document.getElementById("wrapper5"); + grid = wrapper.getGridFragments()[0]; + + // Test that lineName-reversed implicit areas have the correct names. + for (let i = 0; i < grid.areas.length; i++) { + let area = grid.areas[i]; + // test the resulting start/end lines of the areas has: + // Start line: [a-end] + // End Line: [a-start] + if (area.name == "a") { + const startLineNames = grid.cols.lines[area.columnStart - 1].names; + const endLineNames = grid.cols.lines[area.columnEnd - 1].names; + + isnot(startLineNames.indexOf("a-end"), -1, + `Grid column line ${area.columnStart} has the name a-end`); + is(startLineNames.indexOf("a-start"), -1, + `Grid column line ${area.columnStart} does not have the name a-start`); + + isnot(endLineNames.indexOf("a-start"), -1, + `Grid column line ${area.columnEnd} has the name a-start`); + todo_isnot(endLineNames.indexOf("a-end"), -1, + `Grid column line ${area.columnEnd} has the name a-end`); + + // Also test that the fourth line has the name a-end. + todo_isnot(grid.cols.lines[3].names.indexOf("a-end"), -1, + "Grid column line 4 has the line name a-end"); + } + } + + // Test the sixth grid wrapper + wrapper = document.getElementById("wrapper6"); + grid = wrapper.getGridFragments()[0]; + + // Test that the grid has two explicit lines: [foo-end][foo-start] and a third implicit + // line that follows. + is(grid.cols.lines.length, 3, "Grid should have three lines."); + + isnot(grid.cols.lines[0].names.indexOf("foo-end"), -1, + "Grid column line 1 has the name 'foo-end'", + ); + + isnot(grid.cols.lines[1].names.indexOf("foo-start"), -1, + "Grid column line 2 has the name 'foo-start'", + ); + + is(grid.cols.lines[2].type, "implicit", "Grid column line 3 is implicit"); + + SimpleTest.finish(); +} +</script> +</head> +<body onLoad="runTests();"> + + <div id="wrapper1" class="wrapper template1"> + <div id="boxA" class="box a">A</div> + <div id="boxB" class="box b">B</div> + <div id="boxC" class="box c">C</div> + <div id="boxD" class="box d">D</div> + </div> + + <br/> + + <div id="wrapper2" class="wrapper template2"> + <div id="boxA" class="box a">A</div> + </div> + + <br/> + + <div id="wrapper3" class="wrapper template3"> + <div id="boxC" class="box c">C</div> + </div> + + <div id="wrapper4" class="wrapper template4"> + <div id="boxE" class="box e">E</div> + </div> + + <div id="wrapper5" class="wrapper template5"> + <div id="boxF" class="box f">F</div> + <div id="boxG" class="box g">G</div> + </div> + <div id="wrapper6" class="wrapper template6"> + <div id="boxH" class="box h">H</div> + </div> +</body> +</html> diff --git a/dom/grid/test/chrome/test_grid_line_numbers.html b/dom/grid/test/chrome/test_grid_line_numbers.html new file mode 100644 index 0000000000..b5c5b5e534 --- /dev/null +++ b/dom/grid/test/chrome/test_grid_line_numbers.html @@ -0,0 +1,122 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +<style> +body { + margin: 40px; +} +.wrapper { + display: grid; + grid-gap: 0px; + background-color: #f00; +} +.wrapper > div { + background-color: #444; + color: #fff; +} +.repeatColumns { + width: 400px; + grid-auto-columns: 50px; + grid-template-columns: repeat(auto-fit, 100px); +} +.repeatRows { + height: 400px; + grid-auto-rows: 50px; + grid-template-rows: repeat(auto-fit, 100px); +} +</style> + +<script> +"use strict"; + +SimpleTest.waitForExplicitFinish(); + +function testLines(elementName, lines, expectedValues) { + is(lines.count, expectedValues.count, elementName + " has expected number of lines."); + for (let i = 0; i < lines.length; i++) { + is(lines[i].number, expectedValues[i].number, elementName + " line index " + i + " has expected number."); + is(lines[i].negativeNumber, expectedValues[i].negativeNumber, elementName + " line index " + i + " has expected negativeNumber."); + } +} + +function runTests() { + let grid; + let lines; + let expectedValues; + + grid = document.getElementById("gridWithColumns").getGridFragments()[0]; + lines = grid.cols.lines; + expectedValues = [ + { "number": 0, + "negativeNumber": -9 }, + { "number": 0, + "negativeNumber": -8 }, + { "number": 0, + "negativeNumber": -7 }, + { "number": 0, + "negativeNumber": -6 }, + { "number": 1, + "negativeNumber": -5 }, + { "number": 2, + "negativeNumber": -4 }, + { "number": 3, + "negativeNumber": -3 }, + { "number": 4, + "negativeNumber": -2 }, + { "number": 5, + "negativeNumber": -1 }, + { "number": 6, + "negativeNumber": 0 }, + ]; + testLines("gridWithColumns", lines, expectedValues); + + + grid = document.getElementById("gridWithRows").getGridFragments()[0]; + lines = grid.rows.lines; + expectedValues = [ + { "number": 0, + "negativeNumber": -9 }, + { "number": 0, + "negativeNumber": -8 }, + { "number": 0, + "negativeNumber": -7 }, + { "number": 0, + "negativeNumber": -6 }, + { "number": 1, + "negativeNumber": -5 }, + { "number": 2, + "negativeNumber": -4 }, + { "number": 3, + "negativeNumber": -3 }, + { "number": 4, + "negativeNumber": -2 }, + { "number": 5, + "negativeNumber": -1 }, + { "number": 6, + "negativeNumber": 0 }, + ]; + testLines("gridWithRows", lines, expectedValues); + + SimpleTest.finish(); +} +</script> +</head> +<body onLoad="runTests();"> + +<div id="gridWithColumns" class="wrapper repeatColumns"> +<div style="grid-column: -9">A</div> +<div style="grid-column: 2">B</div> +<div style="grid-column: 5">C</div> +</div> + +<div id="gridWithRows" class="wrapper repeatRows"> +<div style="grid-row: span 4 / 1">A</div> +<div style="grid-row: 3">B</div> +<div style="grid-row: 4 / span 2">C</div> +</div> + +</body> +</html> diff --git a/dom/grid/test/chrome/test_grid_lines.html b/dom/grid/test/chrome/test_grid_lines.html new file mode 100644 index 0000000000..c8fa8b6850 --- /dev/null +++ b/dom/grid/test/chrome/test_grid_lines.html @@ -0,0 +1,104 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +<style> +body { + margin: 40px; +} +.wrapper { + display: grid; + width: 400px; + grid-gap: 10px; + grid-template-columns: 50px [first first] repeat(3, [divider] 100px) [last last last last]; + grid-template-rows: [top1 top2] repeat(1, [top2 top2] 50px) [bot]; + background-color: #f00; +} +.box { + background-color: #444; + color: #fff; +} +</style> + +<script> +"use strict"; + +SimpleTest.waitForExplicitFinish(); + +function runTests() { + var wrapper = document.getElementById("wrapper"); + var grid = wrapper.getGridFragments()[0]; + + // test property existence + isnot(typeof(grid.cols.lines), "undefined", "Grid.cols.lines property exists."); + + if (typeof(grid.cols.lines) != "undefined") { + // test column line count + is(grid.cols.lines.length, 5, + "Grid.cols.lines property has length that matches grid-template-columns." + ); + + if (grid.cols.lines.length == 5) { + // test column line position + is(grid.cols.lines[1].start, 50, "Grid column line 2 position is as expected."); + + // test column line width + is(grid.cols.lines[1].breadth, 10, "Grid column line 2 width is as expected."); + + // test column line numbers, positive and negative + is(grid.cols.lines[3].number, 4, "Grid column line 4 positive number is as expected."); + is(grid.cols.lines[3].negativeNumber, -2, "Grid column line 4 negative number is as expected."); + + // test column line names + is(grid.cols.lines[0].names.length, 0, "Grid column line 1 has no names."); + + is(grid.cols.lines[1].names.length, 2, "Grid column line 2 has 2 names."); + is(grid.cols.lines[1].names + "", "first,divider", "Grid column line 2 has the names 'first,divider'."); + + is(grid.cols.lines[4].names.length, 1, "Grid column line 5 has 1 name."); + is(grid.cols.lines[4].names + "", "last", "Grid column line 5 has the name 'last'."); + } + } + + // test property existence + isnot(typeof(grid.rows.lines), "undefined", "Grid.rows.lines property exists."); + + if (typeof(grid.rows.lines) != "undefined") { + // test column line count + is(grid.rows.lines.length, 3, + "Grid.rows.lines property has length that matches grid-template-rows." + ); + + if (grid.rows.lines.length == 3) { + // test row line names + is(grid.rows.lines[0].names.length, 2, "Grid row line 1 has 2 names."); + is(grid.rows.lines[0].names + "", "top1,top2", "Grid row line 1 has the names 'top1,top2'."); + + is(grid.rows.lines[1].names.length, 1, "Grid row line 2 has 1 name."); + is(grid.rows.lines[1].names + "", "bot", "Grid row line 2 has the name 'bot'."); + + is(grid.rows.lines[2].names.length, 0, "Grid row line 3 has no names."); + } + } + + SimpleTest.finish(); +} +</script> +</head> +<body onLoad="runTests();"> + + <div id="wrapper" class="wrapper"> + <div id="boxA" class="box a">A</div> + <div id="boxB" class="box b">B</div> + <div id="boxC" class="box c">C</div> + <div class="box d">D</div> + <div class="box e">E</div> + <div class="box f">F</div> + <div class="box g">G</div> + <div class="box h">H</div> + </div> + +</body> +</html> diff --git a/dom/grid/test/chrome/test_grid_object.html b/dom/grid/test/chrome/test_grid_object.html new file mode 100644 index 0000000000..ab8b9abfa0 --- /dev/null +++ b/dom/grid/test/chrome/test_grid_object.html @@ -0,0 +1,114 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +<style> +body { + margin: 40px; +} +.wrapper { + display: grid; + width: 400px; + grid-gap: 10px; + grid-template-columns: 100px 1fr 1fr 100px; +} +.oh { + overflow: hidden; +} +.box { + background-color: #444; + color: #fff; +} +</style> + +<script> +"use strict"; + +SimpleTest.waitForExplicitFinish(); + +function runTests() { + // Test 1: elements styled with display:grid + let idsWithGrid = [ + "gridDiv", + "gridFieldset", + "gridButton", + "gridDivOh", + "gridFieldsetOh", + "gridButtonOh", + ]; + + for (let id of idsWithGrid) { + let wrapper = document.getElementById(id); + + // test function existence + is(typeof(wrapper.getGridFragments), "function", + id + ": getGridFragments function exists." + ); + + // test that wrapper has one grid + let gridFragments = wrapper.getGridFragments(); + is(gridFragments.length, 1, + id + ": one grid on an un-fragmented display:grid styled element." + ); + + // test that the grid has cols and rows properties + if (gridFragments.length) { + let grid = gridFragments[0]; + isnot(typeof(grid.cols), "undefined", id + ": Grid.cols property exists."); + isnot(typeof(grid.rows), "undefined", id + ": Grid.rows property exists."); + } + } + + // Test 2: elements styled without display:grid + let idsWithoutGrid = [ + "boxA", + ]; + + for (let id of idsWithoutGrid) { + let wrapper = document.getElementById(id); + + // test that wrapper has no grid + let gridFragments = wrapper.getGridFragments(); + is(gridFragments.length, 0, + id + ": no grid on element." + ); + } + + SimpleTest.finish(); +} +</script> +</head> +<body onLoad="runTests();"> + + <div id="gridDiv" class="wrapper"> + <div id="boxA" class="box">A</div> + </div> + + <fieldset id="gridFieldset" class="wrapper"> + <legend>a fieldset</legend> + <label for="name">name</label> + <input id="name"> + </fieldset> + + <button id="gridButton" class="wrapper"> + <span style="grid-row:2">test</span> + </button> + + <div id="gridDivOh" class="wrapper oh"> + <div id="boxAOh" class="box">A</div> + </div> + + <fieldset id="gridFieldsetOh" class="wrapper oh"> + <legend>a fieldset</legend> + <label for="nameOh">name</label> + <input id="nameOh"> + </fieldset> + + <button id="gridButtonOh" class="wrapper oh"> + <span style="grid-row:2">test</span> + </button> + +</body> +</html> diff --git a/dom/grid/test/chrome/test_grid_repeat_auto_fill.html b/dom/grid/test/chrome/test_grid_repeat_auto_fill.html new file mode 100644 index 0000000000..4a558c434a --- /dev/null +++ b/dom/grid/test/chrome/test_grid_repeat_auto_fill.html @@ -0,0 +1,648 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +<style> +body { + margin: 40px; +} +.wrapper { + display: grid; + width: 600px; + grid-gap: 0px; + grid-auto-column: 50px; + background-color: #f00; +} +.grid1 { + grid-template-columns: 50px 0px repeat(auto-fill, 100px); +} +.grid2 { + grid-template-columns: 50px 0px [real-before] repeat(auto-fill, [before] 100px [after]) [real-after] 0px [final]; +} +.grid3 { + grid-template-columns: repeat(3, 66px) [real-before] repeat(auto-fill, [before] 100px [after]) [real-after]; +} +.grid4 { + grid-template-columns: repeat(2, 100px) repeat(auto-fill, 50px); +} +.grid5 { + grid-template-columns: [real-before] repeat(auto-fill, [before] 100px [after]) [real-after]; +} +.grid6 { + grid-template-columns: [first] 0px [real-before] repeat(auto-fill, [before] 100px [after]) [real-after]; +} +.grid7 { + grid-template-columns: [real-before before] repeat(auto-fill, [before] 100px [after]) [after real-after] 0px [final]; +} +.grid8 { + grid-template-columns: [real-before] repeat(auto-fill, [before] 1000px [after]) [real-after]; +} +.grid9 { + grid-template-columns: [real-before] repeat(auto-fill, 100px [after]) [real-after]; +} +.grid10 { + grid-template-columns: [real-before] repeat(auto-fill, [before] 100px) [real-after]; +} +.grid11 { + grid-template-columns: 10px [real-before] repeat(auto-fill, [before] 1000px [after]) [real-after]; +} +.grid12 { + grid-template-columns: 10px [real-before] repeat(auto-fill, [before] 1000px [after]) [real-after] 10px; +} +.grid13 { + grid-template-columns: 10px [real-before] repeat(auto-fill, [before] 1000px [after]) 10px; +} +.grid14 { + grid-template-columns: [real-before] repeat(auto-fill, [before] 1000px [after]) 10px; +} +.grid15 { + grid-template-columns: repeat(auto-fill, [before] 1000px [after]) 10px; +} +.grid16 { + grid-template-columns: repeat(auto-fill, [before] 1000px [after]) [real-after] 10px; +} +.grid17 { + grid-template-columns: repeat(auto-fill, [before] 1000px [after]) [real-after] 10px [final]; +} +.grid18 { + grid-template-columns: repeat(auto-fill, [before] 1000px [after]) 10px [final]; +} +.grid19 { + grid-template-columns: repeat(auto-fill, [before] 1000px); +} +.grid20 { + grid-column: 4 / -1; + grid-template-columns: subgrid repeat(auto-fill, [x]); +} +.grid21 { + grid-column: 4 / -1; + grid-template-columns: subgrid [a] repeat(auto-fill, [x]) [b]; +} + +.box { + background-color: #444; + color: #fff; +} +.a { + grid-column: auto; +} +.b { + grid-column: 4; +} +.c { + grid-column: 6; +} +.d { + grid-column: 7; +} +.e { + grid-column: 5; +} +.f { + grid-column: -9; +} + +</style> + +<script> +"use strict"; + +SimpleTest.waitForExplicitFinish(); + +function testLines(elementName, grid, expectedValues) { + is(grid.cols.lines.length, expectedValues.length, elementName + " has expected number of lines."); + + for (let i = 0; i < grid.cols.lines.length; i++) { + // 'number' is optional. + if (typeof(expectedValues[i].number) != "undefined") { + is(grid.cols.lines[i].number, expectedValues[i].number, elementName + " line " + (i + 1) + " has expected number."); + } else { + // If 'number' is omitted, assume that first line is line 1 and increase from there. + is(grid.cols.lines[i].number, (i + 1), elementName + " line " + (i + 1) + " has expected number."); + } + + // 'negativeNumber' is optional. + if (typeof(expectedValues[i].negativeNumber) != "undefined") { + // Check for the supplied number. + is(grid.cols.lines[i].negativeNumber, expectedValues[i].negativeNumber, elementName + " line " + (i + 1) + " has expected negativeNumber."); + } + + // 'start' is optional. + if (typeof(expectedValues[i].start) != "undefined") { + is(grid.cols.lines[i].start, expectedValues[i].start, elementName + " line " + (i + 1) + " has expected start."); + } + + // 'breadth' is optional. + if (typeof(expectedValues[i].breadth) != "undefined") { + is(grid.cols.lines[i].breadth, 0, elementName + " line " + (i + 1) + " has zero breadth."); + } + + // 'names' is optional. + if (typeof(expectedValues[i].names) != "undefined") { + is(grid.cols.lines[i].names + "", expectedValues[i].names, elementName + " line " + (i + 1) + " has expected names."); + } + + // 'todo_names' is optional. + if (typeof(expectedValues[i].todo_names) != "undefined") { + todo_is(grid.cols.lines[i].names + "", expectedValues[i].todo_names, elementName + " line " + (i + 1) + " has expected names."); + } + } +} + +function runTests() { + let wrapper; + let grid; + let expectedValues; + + wrapper = document.getElementById("wrapper1"); + grid = wrapper.getGridFragments()[0]; + + // test auto-fill count + is(grid.cols.tracks.length, 7, "Grid column track array reports removed auto-fill columns."); + + // test resolved value of grid-template-columns + let templateColumnsText = getComputedStyle(wrapper).gridTemplateColumns; + is(templateColumnsText, "50px 0px 100px 100px 100px 100px 100px", + "Resolved value of grid-template-columns is as expected."); + + // test starts, breadths, and states + expectedValues = [ + { "start": 0, + "breadth": 50, + "state": "static" }, + { "start": 50, + "breadth": 0, + "state": "static" }, + { "start": 50, + "breadth": 100, + "state": "repeat" }, + { "start": 150, + "breadth": 100, + "state": "repeat" }, + { "start": 250, + "breadth": 100, + "state": "repeat" }, + { "start": 350, + "breadth": 100, + "state": "repeat" }, + { "start": 450, + "breadth": 100, + "state": "repeat" }, + ]; + for (let i = 0; i < grid.cols.tracks.length; i++) { + is(grid.cols.tracks[i].start, expectedValues[i].start, "Column " + (i + 1) + " has expected start."); + is(grid.cols.tracks[i].breadth, expectedValues[i].breadth, "Column " + (i + 1) + " has expected breadth."); + is(grid.cols.tracks[i].state, expectedValues[i].state, "Column " + (i + 1) + " has expected state."); + } + + + wrapper = document.getElementById("wrapper2"); + grid = wrapper.getGridFragments()[0]; + + // test auto-fill count + is(grid.cols.lines.length, 9, "Grid column line array reports removed auto-fill columns."); + + // test resolved value of grid-template-columns + templateColumnsText = getComputedStyle(wrapper).gridTemplateColumns; + is(templateColumnsText, "50px 0px [real-before before] 100px [after before] 100px [after before] 100px [after before] 100px [after before] 100px [after real-after] 0px [final]", + "Resolved value of grid-template-columns is as expected."); + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "" }, + { "start": 50, + "names": "" }, + { "start": 50, + "names": "real-before,before" }, + { "start": 150, + "names": "after,before" }, + { "start": 250, + "names": "after,before" }, + { "start": 350, + "names": "after,before" }, + { "start": 450, + "names": "after,before" }, + { "start": 550, + "names": "after,real-after" }, + { "start": 550, + "names": "final" }, + ]; + testLines("wrapper2", grid, expectedValues); + + + wrapper = document.getElementById("wrapper3"); + grid = wrapper.getGridFragments()[0]; + + // test resolved value of grid-template-columns + templateColumnsText = getComputedStyle(wrapper).gridTemplateColumns; + is(templateColumnsText, "66px 66px 66px [real-before before] 100px [after before] 100px [after before] 100px [after before] 100px [after real-after]", + "Resolved value of grid-template-columns is as expected."); + + + wrapper = document.getElementById("wrapper4"); + grid = wrapper.getGridFragments()[0]; + + // test auto-fill count of tracks + is(grid.cols.tracks.length, 10, "Grid column track array respects auto-fill columns."); + + if (grid.cols.tracks.length == 10) { + // test for static and repeat + is(grid.cols.tracks[1].state, "static", "Grid column track 2 is marked as static."); + is(grid.cols.tracks[2].state, "repeat", "Grid column track 3 is marked as repeat."); + } + + + wrapper = document.getElementById("wrapper5"); + grid = wrapper.getGridFragments()[0]; + + // test resolved value of grid-template-columns + templateColumnsText = getComputedStyle(wrapper).gridTemplateColumns; + is(templateColumnsText, "[real-before before] 100px [after before] 100px [after before] 100px [after before] 100px [after before] 100px [after before] 100px [after real-after]", "Resolved value of grid-template-columns is as expected."); + + + wrapper = document.getElementById("wrapper6"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "first" }, + { "start": 0, + "names": "real-before,before" }, + { "start": 100, + "names": "after,before" }, + { "start": 200, + "names": "after,before" }, + { "start": 300, + "names": "after,before" }, + { "start": 400, + "names": "after,before" }, + { "start": 500, + "names": "after,before" }, + { "start": 600, + "names": "after,real-after" }, + ]; + testLines("wrapper6", grid, expectedValues); + + + wrapper = document.getElementById("wrapper7"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "real-before,before" }, + { "start": 100, + "names": "after,before" }, + { "start": 200, + "names": "after,before" }, + { "start": 300, + "names": "after,before" }, + { "start": 400, + "names": "after,before" }, + { "start": 500, + "names": "after,before" }, + { "start": 600, + "names": "after,real-after" }, + { "start": 600, + "names": "final" }, + ]; + testLines("wrapper7", grid, expectedValues); + + + wrapper = document.getElementById("wrapper8"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "real-before,before" }, + { "start": 1000, + "names": "after,real-after" }, + ]; + testLines("wrapper8", grid, expectedValues); + + + wrapper = document.getElementById("wrapper8b"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "number": 0 }, + { "number": 0 }, + { "number": 0 }, + { "number": 0 }, + { "number": 0 }, + { "number": 0 }, + { "number": 0 }, + { "number": 1, + "names": "real-before,before" }, + { "number": 2, + "names": "after,real-after" }, + ]; + testLines("wrapper8b", grid, expectedValues); + + + wrapper = document.getElementById("wrapper9"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "real-before" }, + { "start": 100, + "names": "after" }, + { "start": 200, + "names": "after" }, + { "start": 300, + "names": "after" }, + { "start": 400, + "names": "after" }, + { "start": 500, + "names": "after" }, + { "start": 600, + "names": "after,real-after" }, + ]; + testLines("wrapper9", grid, expectedValues); + + + wrapper = document.getElementById("wrapper10"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "real-before,before" }, + { "start": 100, + "names": "before" }, + { "start": 200, + "names": "before" }, + { "start": 300, + "names": "before" }, + { "start": 400, + "names": "before" }, + { "start": 500, + "names": "before" }, + { "start": 600, + "names": "real-after" }, + ]; + testLines("wrapper10", grid, expectedValues); + + wrapper = document.getElementById("wrapper11"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "" }, + { "start": 10, + "names": "real-before,before" }, + { "start": 1010, + "names": "after,real-after" }, + ]; + testLines("wrapper11", grid, expectedValues); + + wrapper = document.getElementById("wrapper12"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "" }, + { "start": 10, + "names": "real-before,before" }, + { "start": 1010, + "names": "after,real-after" }, + { "start": 1020, + "names": "" }, + ]; + testLines("wrapper12", grid, expectedValues); + + wrapper = document.getElementById("wrapper13"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "" }, + { "start": 10, + "names": "real-before,before" }, + { "start": 1010, + "names": "after" }, + { "start": 1020, + "names": "" }, + ]; + testLines("wrapper13", grid, expectedValues); + + wrapper = document.getElementById("wrapper14"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "real-before,before" }, + { "start": 1000, + "names": "after" }, + { "start": 1010, + "names": "" }, + ]; + testLines("wrapper14", grid, expectedValues); + + wrapper = document.getElementById("wrapper15"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "before" }, + { "start": 1000, + "names": "after" }, + { "start": 1010, + "names": "" }, + ]; + testLines("wrapper15", grid, expectedValues); + + wrapper = document.getElementById("wrapper16"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "before" }, + { "start": 1000, + "names": "after,real-after" }, + { "start": 1010, + "names": "" }, + ]; + testLines("wrapper16", grid, expectedValues); + + wrapper = document.getElementById("wrapper17"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "before" }, + { "start": 1000, + "names": "after,real-after" }, + { "start": 1010, + "names": "final" }, + ]; + testLines("wrapper17", grid, expectedValues); + + wrapper = document.getElementById("wrapper18"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "before" }, + { "start": 1000, + "names": "after" }, + { "start": 1010, + "names": "final" }, + ]; + testLines("wrapper18", grid, expectedValues); + + wrapper = document.getElementById("wrapper19"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "before" }, + { "start": 1000, + "names": "" }, + ]; + testLines("wrapper19", grid, expectedValues); + + wrapper = document.getElementById("wrapper20"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "names": "x" }, + { "names": "x" }, + { "names": "x" }, + { "names": "x" }, + ]; + testLines("wrapper20", grid, expectedValues); + + wrapper = document.getElementById("wrapper21"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "names": "a" }, + { "names": "x" }, + { "names": "x" }, + { "names": "b" }, + ]; + testLines("wrapper21", grid, expectedValues); + + SimpleTest.finish(); +} +</script> +</head> +<body onLoad="runTests();"> + + <div id="wrapper1" class="wrapper grid1"> + <div id="boxB" class="box b">B</div> + </div> + + <br/> + <div id="wrapper2" class="wrapper grid2"> + <div id="boxB" class="box b">B</div> + <div id="boxC" class="box c">C</div> + </div> + + <br/> + <div id="wrapper3" class="wrapper grid3"> + <div id="boxB" class="box b">B</div> + <div id="boxC" class="box c">C</div> + <div id="boxD" class="box d">D</div> + </div> + + <br/> + <div id="wrapper4" class="wrapper grid4"> + <div id="boxA" class="box a">A</div> + </div> + + <br/> + <div id="wrapper5" class="wrapper grid5"> + </div> + + <br/> + <div id="wrapper6" class="wrapper grid6"> + <div id="boxB" class="box b">B</div> + </div> + + <br/> + <div id="wrapper7" class="wrapper grid7"> + <div id="boxB" class="box b">B</div> + </div> + + <br/> + <div id="wrapper8" class="wrapper grid8"> + </div> + + <br/> + <div id="wrapper8b" class="wrapper grid8"> + <div id="boxF" class="box f">F</div> + </div> + + <br/> + <div id="wrapper9" class="wrapper grid9"> + <div id="boxB" class="box b">B</div> + <div id="boxE" class="box e">E</div> + <div id="wrapper20" class="wrapper grid20"><x style="grid-column:x">x</x></div> + </div> + + <br/> + <div id="wrapper10" class="wrapper grid10"> + <div id="boxB" class="box b">B</div> + <div id="boxE" class="box e">E</div> + <div id="wrapper21" class="wrapper grid21"><x style="grid-column:y">y</x></div> + </div> + + <br/> + <div id="wrapper11" class="wrapper grid11"> + </div> + + <br/> + <div id="wrapper12" class="wrapper grid12"> + </div> + + <br/> + <div id="wrapper13" class="wrapper grid13"> + </div> + + <br/> + <div id="wrapper14" class="wrapper grid14"> + </div> + + <br/> + <div id="wrapper15" class="wrapper grid15"> + </div> + + <br/> + <div id="wrapper16" class="wrapper grid16"> + </div> + + <br/> + <div id="wrapper17" class="wrapper grid17"> + </div> + + <br/> + <div id="wrapper18" class="wrapper grid18"> + </div> + + <br/> + <div id="wrapper19" class="wrapper grid19"> + </div> + +</body> +</html> diff --git a/dom/grid/test/chrome/test_grid_repeat_auto_fit.html b/dom/grid/test/chrome/test_grid_repeat_auto_fit.html new file mode 100644 index 0000000000..6643521d43 --- /dev/null +++ b/dom/grid/test/chrome/test_grid_repeat_auto_fit.html @@ -0,0 +1,614 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +<style> +body { + margin: 40px; +} +.wrapper { + display: grid; + width: 600px; + grid-gap: 0px; + grid-auto-column: 50px; + background-color: #f00; +} +.grid1 { + grid-template-columns: 50px 0px repeat(auto-fit, 100px); +} +.grid2 { + grid-template-columns: 50px 0px [real-before] repeat(auto-fit, [before] 100px [after]) [real-after] 0px [final]; +} +.grid3 { + grid-template-columns: repeat(3, 66px) [real-before] repeat(auto-fit, [before] 100px [after]) [real-after]; +} +.grid4 { + grid-template-columns: repeat(2, 100px) repeat(auto-fill, 50px); +} +.grid5 { + grid-template-columns: [real-before] repeat(auto-fit, [before] 100px [after]) [real-after]; +} +.grid6 { + grid-template-columns: [first] 0px [real-before] repeat(auto-fit, [before] 100px [after]) [real-after]; +} +.grid7 { + grid-template-columns: [real-before before] repeat(auto-fit, [before] 100px [after]) [after real-after] 0px [final]; +} +.grid8 { + grid-template-columns: [real-before] repeat(auto-fit, [before] 1000px [after]) [real-after]; +} +.grid9 { + grid-template-columns: [real-before] repeat(auto-fit, 100px [after]) [real-after]; +} +.grid10 { + grid-template-columns: [real-before] repeat(auto-fit, [before] 100px) [real-after]; +} +.grid11 { + grid-template-columns: 10px [real-before] repeat(auto-fit, [before] 1000px [after]) [real-after]; +} +.grid12 { + grid-template-columns: 10px [real-before] repeat(auto-fit, [before] 1000px [after]) [real-after] 10px; +} +.grid13 { + grid-template-columns: 10px [real-before] repeat(auto-fit, [before] 1000px [after]) 10px; +} +.grid14 { + grid-template-columns: [real-before] repeat(auto-fit, [before] 1000px [after]) 10px; +} +.grid15 { + grid-template-columns: repeat(auto-fit, [before] 1000px [after]) 10px; +} +.grid16 { + grid-template-columns: repeat(auto-fit, [before] 1000px [after]) [real-after] 10px; +} +.grid17 { + grid-template-columns: repeat(auto-fit, [before] 1000px [after]) [real-after] 10px [final]; +} +.grid18 { + grid-template-columns: repeat(auto-fit, [before] 1000px [after]) 10px [final]; +} +.grid19 { + grid-template-columns: repeat(auto-fit, [before] 1000px); +} + +.box { + background-color: #444; + color: #fff; +} +.a { + grid-column: auto; +} +.b { + grid-column: 4; +} +.c { + grid-column: 6; +} +.d { + grid-column: 7; +} +.e { + grid-column: 5; +} +.f { + grid-column: -9; +} + +</style> + +<script> +"use strict"; + +SimpleTest.waitForExplicitFinish(); + +function testLines(elementName, grid, expectedValues) { + is(grid.cols.lines.length, expectedValues.length, elementName + " has expected number of lines."); + + for (let i = 0; i < grid.cols.lines.length; i++) { + // 'number' is optional. + if (typeof(expectedValues[i].number) != "undefined") { + is(grid.cols.lines[i].number, expectedValues[i].number, elementName + " line " + (i + 1) + " has expected number."); + } else { + // If 'number' is omitted, assume that first line is line 1 and increase from there. + is(grid.cols.lines[i].number, (i + 1), elementName + " line " + (i + 1) + " has expected number."); + } + + // 'negativeNumber' is optional. + if (typeof(expectedValues[i].negativeNumber) != "undefined") { + // Check for the supplied number. + is(grid.cols.lines[i].negativeNumber, expectedValues[i].negativeNumber, elementName + " line " + (i + 1) + " has expected negativeNumber."); + } + + // 'start' is optional. + if (typeof(expectedValues[i].start) != "undefined") { + is(grid.cols.lines[i].start, expectedValues[i].start, elementName + " line " + (i + 1) + " has expected start."); + } + + // 'breadth' is optional. + if (typeof(expectedValues[i].breadth) != "undefined") { + is(grid.cols.lines[i].breadth, 0, elementName + " line " + (i + 1) + " has zero breadth."); + } + + // 'names' is optional. + if (typeof(expectedValues[i].names) != "undefined") { + is(grid.cols.lines[i].names + "", expectedValues[i].names, elementName + " line " + (i + 1) + " has expected names."); + } + + // 'todo_names' is optional. + if (typeof(expectedValues[i].todo_names) != "undefined") { + todo_is(grid.cols.lines[i].names + "", expectedValues[i].todo_names, elementName + " line " + (i + 1) + " has expected names."); + } + } +} + +function runTests() { + let wrapper; + let grid; + let expectedValues; + + wrapper = document.getElementById("wrapper1"); + grid = wrapper.getGridFragments()[0]; + + // test auto-fit count + is(grid.cols.tracks.length, 7, "Grid column track array reports removed auto-fit columns."); + + // test resolved value of grid-template-columns + let templateColumnsText = getComputedStyle(wrapper).gridTemplateColumns; + is(templateColumnsText, "50px 0px 0px 100px 0px 0px 0px", + "Resolved value of grid-template-columns reports removed auto-fits as '0px'."); + + // test starts, breadths, and states + expectedValues = [ + { "start": 0, + "breadth": 50, + "state": "static" }, + { "start": 50, + "breadth": 0, + "state": "static" }, + { "start": 50, + "breadth": 0, + "state": "removed" }, + { "start": 50, + "breadth": 100, + "state": "repeat" }, + { "start": 150, + "breadth": 0, + "state": "removed" }, + { "start": 150, + "breadth": 0, + "state": "removed" }, + { "start": 150, + "breadth": 0, + "state": "removed" }, + ]; + for (let i = 0; i < grid.cols.tracks.length; i++) { + is(grid.cols.tracks[i].start, expectedValues[i].start, "Column " + (i + 1) + " has expected start."); + is(grid.cols.tracks[i].breadth, expectedValues[i].breadth, "Column " + (i + 1) + " has expected breadth."); + is(grid.cols.tracks[i].state, expectedValues[i].state, "Column " + (i + 1) + " has expected state."); + } + + + wrapper = document.getElementById("wrapper2"); + grid = wrapper.getGridFragments()[0]; + + // test auto-fit count + is(grid.cols.lines.length, 9, "Grid column line array reports removed auto-fit columns."); + + // test resolved value of grid-template-columns + templateColumnsText = getComputedStyle(wrapper).gridTemplateColumns; + is(templateColumnsText, "50px 0px [real-before before] 0px [after before] 100px [after before] 0px [after before] 100px [after before] 0px [after real-after] 0px [final]", + "Resolved value of grid-template-columns reports lines for removed tracks."); + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "" }, + { "start": 50, + "names": "" }, + { "start": 50, + "names": "real-before,before" }, + { "start": 50, + "names": "after,before" }, + { "start": 150, + "names": "after,before" }, + { "start": 150, + "names": "after,before" }, + { "start": 250, + "names": "after,before" }, + { "start": 250, + "names": "after,real-after" }, + { "start": 250, + "names": "final" }, + ]; + testLines("wrapper2", grid, expectedValues); + + + wrapper = document.getElementById("wrapper3"); + grid = wrapper.getGridFragments()[0]; + + // test resolved value of grid-template-columns + templateColumnsText = getComputedStyle(wrapper).gridTemplateColumns; + is(templateColumnsText, "66px 66px 66px [real-before before] 100px [after before] 0px [after before] 100px [after before] 100px [after real-after]", + "Resolved value of grid-template-columns reports lines for removed tracks."); + + + wrapper = document.getElementById("wrapper4"); + grid = wrapper.getGridFragments()[0]; + + // test auto-fill count of tracks + is(grid.cols.tracks.length, 10, "Grid column track array respects auto-fill columns."); + + if (grid.cols.tracks.length == 10) { + // test for static and repeat + is(grid.cols.tracks[1].state, "static", "Grid column track 2 is marked as static."); + is(grid.cols.tracks[2].state, "repeat", "Grid column track 3 is marked as repeat."); + } + + + wrapper = document.getElementById("wrapper5"); + grid = wrapper.getGridFragments()[0]; + + // test resolved value of grid-template-columns + templateColumnsText = getComputedStyle(wrapper).gridTemplateColumns; + is(templateColumnsText, "[real-before before] 0px [after before] 0px [after before] 0px [after before] 0px [after before] 0px [after before] 0px [after real-after]", "Resolved value of grid-template-columns no longer lists 'none' when all auto-fit tracks are empty."); + + + wrapper = document.getElementById("wrapper6"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "first" }, + { "start": 0, + "names": "real-before,before" }, + { "start": 0, + "names": "after,before" }, + { "start": 0, + "names": "after,before" }, + { "start": 100, + "names": "after,before" }, + { "start": 100, + "names": "after,before" }, + { "start": 100, + "names": "after,before" }, + { "start": 100, + "names": "after,real-after" }, + ]; + testLines("wrapper6", grid, expectedValues); + + + wrapper = document.getElementById("wrapper7"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "real-before,before" }, + { "start": 0, + "names": "after,before" }, + { "start": 0, + "names": "after,before" }, + { "start": 0, + "names": "after,before" }, + { "start": 100, + "names": "after,before" }, + { "start": 100, + "names": "after,before" }, + { "start": 100, + "names": "after,real-after" }, + { "start": 100, + "names": "final" }, + ]; + testLines("wrapper7", grid, expectedValues); + + + wrapper = document.getElementById("wrapper8"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "real-before,before" }, + { "start": 0, + "names": "after,real-after" }, + ]; + testLines("wrapper8", grid, expectedValues); + + + wrapper = document.getElementById("wrapper8b"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "number": 0 }, + { "number": 0 }, + { "number": 0 }, + { "number": 0 }, + { "number": 0 }, + { "number": 0 }, + { "number": 0 }, + { "number": 1, + "names": "real-before,before" }, + { "number": 2, + "names": "after,real-after" }, + ]; + testLines("wrapper8b", grid, expectedValues); + + + wrapper = document.getElementById("wrapper9"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "real-before" }, + { "start": 0, + "names": "after" }, + { "start": 0, + "names": "after" }, + { "start": 0, + "names": "after" }, + { "start": 100, + "names": "after" }, + { "start": 200, + "names": "after" }, + { "start": 200, + "names": "after,real-after" }, + ]; + testLines("wrapper9", grid, expectedValues); + + + wrapper = document.getElementById("wrapper10"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "real-before,before" }, + { "start": 0, + "names": "before" }, + { "start": 0, + "names": "before" }, + { "start": 0, + "names": "before" }, + { "start": 100, + "names": "before" }, + { "start": 200, + "names": "before" }, + { "start": 200, + "names": "real-after" }, + ]; + testLines("wrapper10", grid, expectedValues); + + wrapper = document.getElementById("wrapper11"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "" }, + { "start": 10, + "names": "real-before,before" }, + { "start": 10, + "names": "after,real-after" }, + ]; + testLines("wrapper11", grid, expectedValues); + + wrapper = document.getElementById("wrapper12"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "" }, + { "start": 10, + "names": "real-before,before" }, + { "start": 10, + "names": "after,real-after" }, + { "start": 20, + "names": "" }, + ]; + testLines("wrapper12", grid, expectedValues); + + wrapper = document.getElementById("wrapper13"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "" }, + { "start": 10, + "names": "real-before,before" }, + { "start": 10, + "names": "after" }, + { "start": 20, + "names": "" }, + ]; + testLines("wrapper13", grid, expectedValues); + + wrapper = document.getElementById("wrapper14"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "real-before,before" }, + { "start": 0, + "names": "after" }, + { "start": 10, + "names": "" }, + ]; + testLines("wrapper14", grid, expectedValues); + + wrapper = document.getElementById("wrapper15"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "before" }, + { "start": 0, + "names": "after" }, + { "start": 10, + "names": "" }, + ]; + testLines("wrapper15", grid, expectedValues); + + wrapper = document.getElementById("wrapper16"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "before" }, + { "start": 0, + "names": "after,real-after" }, + { "start": 10, + "names": "" }, + ]; + testLines("wrapper16", grid, expectedValues); + + wrapper = document.getElementById("wrapper17"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "before" }, + { "start": 0, + "names": "after,real-after" }, + { "start": 10, + "names": "final" }, + ]; + testLines("wrapper17", grid, expectedValues); + + wrapper = document.getElementById("wrapper18"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "before" }, + { "start": 0, + "names": "after" }, + { "start": 10, + "names": "final" }, + ]; + testLines("wrapper18", grid, expectedValues); + + wrapper = document.getElementById("wrapper19"); + grid = wrapper.getGridFragments()[0]; + + // test starts and names + expectedValues = [ + { "start": 0, + "names": "before" }, + { "start": 0, + "names": "" }, + ]; + testLines("wrapper19", grid, expectedValues); + + SimpleTest.finish(); +} +</script> +</head> +<body onLoad="runTests();"> + + <div id="wrapper1" class="wrapper grid1"> + <div id="boxB" class="box b">B</div> + </div> + + <br/> + <div id="wrapper2" class="wrapper grid2"> + <div id="boxB" class="box b">B</div> + <div id="boxC" class="box c">C</div> + </div> + + <br/> + <div id="wrapper3" class="wrapper grid3"> + <div id="boxB" class="box b">B</div> + <div id="boxC" class="box c">C</div> + <div id="boxD" class="box d">D</div> + </div> + + <br/> + <div id="wrapper4" class="wrapper grid4"> + <div id="boxA" class="box a">A</div> + </div> + + <br/> + <div id="wrapper5" class="wrapper grid5"> + </div> + + <br/> + <div id="wrapper6" class="wrapper grid6"> + <div id="boxB" class="box b">B</div> + </div> + + <br/> + <div id="wrapper7" class="wrapper grid7"> + <div id="boxB" class="box b">B</div> + </div> + + <br/> + <div id="wrapper8" class="wrapper grid8"> + </div> + + <br/> + <div id="wrapper8b" class="wrapper grid8"> + <div id="boxF" class="box f">F</div> + </div> + + <br/> + <div id="wrapper9" class="wrapper grid9"> + <div id="boxB" class="box b">B</div> + <div id="boxE" class="box e">E</div> + </div> + + <br/> + <div id="wrapper10" class="wrapper grid10"> + <div id="boxB" class="box b">B</div> + <div id="boxE" class="box e">E</div> + </div> + + <br/> + <div id="wrapper11" class="wrapper grid11"> + </div> + + <br/> + <div id="wrapper12" class="wrapper grid12"> + </div> + + <br/> + <div id="wrapper13" class="wrapper grid13"> + </div> + + <br/> + <div id="wrapper14" class="wrapper grid14"> + </div> + + <br/> + <div id="wrapper15" class="wrapper grid15"> + </div> + + <br/> + <div id="wrapper16" class="wrapper grid16"> + </div> + + <br/> + <div id="wrapper17" class="wrapper grid17"> + </div> + + <br/> + <div id="wrapper18" class="wrapper grid18"> + </div> + + <br/> + <div id="wrapper19" class="wrapper grid19"> + </div> + +</body> +</html> diff --git a/dom/grid/test/chrome/test_grid_subtree_dirty.html b/dom/grid/test/chrome/test_grid_subtree_dirty.html new file mode 100644 index 0000000000..4baa94872e --- /dev/null +++ b/dom/grid/test/chrome/test_grid_subtree_dirty.html @@ -0,0 +1,56 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +<style> + #grid { + display: grid; + width: 100px; + height: 100px; + } + + #initiallyHidden { + display: none; + width: 100px; + height: 100px; + background: lime; + } +</style> +<script> +"use strict"; + +SimpleTest.waitForExplicitFinish(); + +function runTests() { + const grid = document.getElementById("grid"); + const initiallyHidden = document.getElementById("initiallyHidden"); + document.documentElement.offsetHeight; // Flush layout to be sure we have grid-item sizes cached + + grid.getGridFragments(); // This marks the grid and its descendants as dirty + + document.documentElement.offsetHeight; // Flush layout again. In buggy builds, this layout will fail to reflow/clear-dirty-bits on the grid item. + + initiallyHidden.style.display = "block"; // This should trigger a reflow. + let height = getComputedStyle(initiallyHidden).height; + is(height, "100px", "initiallyHidden element should get a reflow and arrive at the expected height after we toggle its display"); + + SimpleTest.finish(); +} +</script> +</head> +<body onLoad="runTests();"> + <div id="grid"> + <div> + <div> + <div> + <div id="initiallyHidden"> + PASS + </div> + </div> + </div> + </div> + </div> +</body> +</html> diff --git a/dom/grid/test/chrome/test_grid_tracks.html b/dom/grid/test/chrome/test_grid_tracks.html new file mode 100644 index 0000000000..39697ef799 --- /dev/null +++ b/dom/grid/test/chrome/test_grid_tracks.html @@ -0,0 +1,85 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +<style> +body { + margin: 40px; +} +.wrapper { + display: grid; + width: 400px; + grid-gap: 10px; + grid-template-columns: 100px 1fr 1fr 100px; + background-color: #f00; +} +.box { + background-color: #444; + color: #fff; +} +</style> + +<script> +"use strict"; + +SimpleTest.waitForExplicitFinish(); + +function runTests() { + var wrapper = document.getElementById("wrapper"); + var grid = wrapper.getGridFragments()[0]; + var boxA = document.getElementById("boxA"); + var boxB = document.getElementById("boxB"); + + // test property existence + isnot(typeof(grid.cols.tracks), "undefined", "Grid.cols.tracks property exists."); + isnot(typeof(grid.rows.tracks), "undefined", "Grid.rows.tracks property exists."); + + if ((typeof(grid.cols.tracks) != "undefined") && + (typeof(grid.rows.tracks) != "undefined")) { + // test column and row track counts + is(grid.cols.tracks.length, 4, + "Grid.cols.tracks property has length that matches grid-template-columns." + ); + is(grid.rows.tracks.length, 2, + "Grid.rows.tracks property has length that matches content." + ); + + if ((grid.cols.tracks.length == 4) && + (grid.rows.tracks.length == 2)) { + // test column track position + is(grid.cols.tracks[1].start, 110, "Grid column track 2 position is as expected."); + + // test column track width + is(grid.cols.tracks[0].breadth, boxA.offsetWidth, + "Grid column track width (fixed size) matches item width." + ); + is(grid.cols.tracks[1].breadth, boxB.offsetWidth, + "Grid column track width (flexible size) matches item width." + ); + is(grid.cols.tracks[1].breadth, grid.cols.tracks[2].breadth, + "Grid column track widths with equal proportion flexible size actually are same size." + ); + } + } + + SimpleTest.finish(); +} +</script> +</head> +<body onLoad="runTests();"> + + <div id="wrapper" class="wrapper"> + <div id="boxA" class="box a">A</div> + <div id="boxB" class="box b">B</div> + <div class="box c">C</div> + <div class="box d">D</div> + <div class="box e">E</div> + <div class="box f">F</div> + <div class="box g">G</div> + <div class="box h">H</div> + </div> + +</body> +</html> |