summaryrefslogtreecommitdiffstats
path: root/dom/grid
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/grid/Grid.cpp119
-rw-r--r--dom/grid/Grid.h51
-rw-r--r--dom/grid/GridArea.cpp51
-rw-r--r--dom/grid/GridArea.h55
-rw-r--r--dom/grid/GridDimension.cpp54
-rw-r--r--dom/grid/GridDimension.h57
-rw-r--r--dom/grid/GridLine.cpp72
-rw-r--r--dom/grid/GridLine.h63
-rw-r--r--dom/grid/GridLines.cpp372
-rw-r--r--dom/grid/GridLines.h65
-rw-r--r--dom/grid/GridTrack.cpp54
-rw-r--r--dom/grid/GridTrack.h50
-rw-r--r--dom/grid/GridTracks.cpp108
-rw-r--r--dom/grid/GridTracks.h51
-rw-r--r--dom/grid/moz.build36
-rw-r--r--dom/grid/test/chrome.toml23
-rw-r--r--dom/grid/test/chrome/test_grid_areas.html143
-rw-r--r--dom/grid/test/chrome/test_grid_equality.html57
-rw-r--r--dom/grid/test/chrome/test_grid_fragmentation.html86
-rw-r--r--dom/grid/test/chrome/test_grid_implicit.html390
-rw-r--r--dom/grid/test/chrome/test_grid_line_numbers.html122
-rw-r--r--dom/grid/test/chrome/test_grid_lines.html104
-rw-r--r--dom/grid/test/chrome/test_grid_object.html114
-rw-r--r--dom/grid/test/chrome/test_grid_repeat_auto_fill.html648
-rw-r--r--dom/grid/test/chrome/test_grid_repeat_auto_fit.html614
-rw-r--r--dom/grid/test/chrome/test_grid_subtree_dirty.html56
-rw-r--r--dom/grid/test/chrome/test_grid_tracks.html85
27 files changed, 3700 insertions, 0 deletions
diff --git a/dom/grid/Grid.cpp b/dom/grid/Grid.cpp
new file mode 100644
index 0000000000..bda04abe4d
--- /dev/null
+++ b/dom/grid/Grid.cpp
@@ -0,0 +1,119 @@
+/* -*- 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_CLASS(Grid)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Grid)
+ tmp->ForgetFrame();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent, mRows, mCols, mAreas)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Grid)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent, mRows, mCols, mAreas)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+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)),
+ mFrame(aFrame),
+ mRows(new GridDimension(this)),
+ mCols(new GridDimension(this)) {
+ MOZ_ASSERT(aFrame,
+ "Should never be instantiated with a null nsGridContainerFrame");
+
+ aFrame->SetProperty(nsGridContainerFrame::GridFragmentInfo(), this);
+
+ // 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);
+}
+
+void Grid::ForgetFrame() {
+ if (mFrame.IsAlive()) {
+ mFrame->RemoveProperty(nsGridContainerFrame::GridFragmentInfo());
+ mFrame.Clear(mFrame->PresContext()->GetPresShell());
+ }
+}
+
+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..d69aa2fe45
--- /dev/null
+++ b/dom/grid/Grid.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_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; }
+
+ void ForgetFrame();
+
+ GridDimension* Rows() const;
+ GridDimension* Cols() const;
+ void GetAreas(nsTArray<RefPtr<GridArea>>& aAreas) const;
+
+ protected:
+ nsCOMPtr<Element> mParent;
+ WeakFrame mFrame;
+ 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..4610f18eab
--- /dev/null
+++ b/dom/grid/GridLines.cpp
@@ -0,0 +1,372 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GridLines.h"
+
+#include "GridDimension.h"
+#include "GridLine.h"
+#include "mozilla/dom/GridBinding.h"
+#include "mozilla/dom/GridArea.h"
+#include "nsGridContainerFrame.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridLines, mParent, mLines)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(GridLines)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(GridLines)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridLines)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+GridLines::GridLines(GridDimension* aParent) : mParent(aParent) {
+ MOZ_ASSERT(aParent, "Should never be instantiated with a null GridDimension");
+}
+
+GridLines::~GridLines() = default;
+
+JSObject* GridLines::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return GridLines_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+uint32_t GridLines::Length() const { return mLines.Length(); }
+
+GridLine* GridLines::Item(uint32_t aIndex) {
+ return mLines.SafeElementAt(aIndex);
+}
+
+GridLine* GridLines::IndexedGetter(uint32_t aIndex, bool& aFound) {
+ aFound = aIndex < mLines.Length();
+ if (!aFound) {
+ return nullptr;
+ }
+ return mLines[aIndex];
+}
+
+static void AddLineNameIfNotPresent(nsTArray<RefPtr<nsAtom>>& aLineNames,
+ nsAtom* aName) {
+ if (!aLineNames.Contains(aName)) {
+ aLineNames.AppendElement(aName);
+ }
+}
+
+static void AddLineNamesIfNotPresent(nsTArray<RefPtr<nsAtom>>& aLineNames,
+ const nsTArray<RefPtr<nsAtom>>& aNames) {
+ for (const auto& name : aNames) {
+ AddLineNameIfNotPresent(aLineNames, name);
+ }
+}
+
+void GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo,
+ const ComputedGridLineInfo* aLineInfo,
+ const nsTArray<RefPtr<GridArea>>& aAreas,
+ bool aIsRow) {
+ MOZ_ASSERT(aLineInfo);
+ mLines.Clear();
+
+ if (!aTrackInfo) {
+ return;
+ }
+
+ uint32_t lineCount =
+ aTrackInfo->mEndFragmentTrack - aTrackInfo->mStartFragmentTrack + 1;
+
+ // If there is at least one track, line count is one more
+ // than the number of tracks.
+ if (lineCount > 0) {
+ nscoord lastTrackEdge = 0;
+ nscoord startOfNextTrack;
+ uint32_t repeatIndex = 0;
+ uint32_t numRepeatTracks = aTrackInfo->mRemovedRepeatTracks.Length();
+ uint32_t numAddedLines = 0;
+
+ // For the calculation of negative line numbers, we need to know
+ // the total number of leading implicit and explicit tracks.
+ // This might be different from the number of tracks sizes in
+ // aTrackInfo, because some of those tracks may be auto-fits that
+ // have been removed.
+ uint32_t leadingTrackCount =
+ aTrackInfo->mNumLeadingImplicitTracks + aTrackInfo->mNumExplicitTracks;
+ if (numRepeatTracks > 0) {
+ for (auto& removedTrack : aTrackInfo->mRemovedRepeatTracks) {
+ if (removedTrack) {
+ ++leadingTrackCount;
+ }
+ }
+ }
+
+ for (uint32_t i = aTrackInfo->mStartFragmentTrack;
+ i < aTrackInfo->mEndFragmentTrack + 1; i++) {
+ // Since line indexes are 1-based, calculate a 1-based value
+ // for this track to simplify some calculations.
+ const uint32_t line1Index = i + 1;
+
+ startOfNextTrack = (i < aTrackInfo->mEndFragmentTrack)
+ ? aTrackInfo->mPositions[i]
+ : lastTrackEdge;
+
+ // Get the line names for the current line. aLineInfo->mNames
+ // may contain duplicate names. This is intentional, since grid
+ // layout works fine with duplicate names, and we don't want to
+ // detect and remove duplicates in layout since it is an O(n^2)
+ // problem. We do the work here since this is only run when
+ // requested by devtools, and slowness here will not affect
+ // normal browsing.
+ nsTArray<RefPtr<nsAtom>> empty{};
+ const nsTArray<RefPtr<nsAtom>>& possiblyDuplicateLineNames(
+ aLineInfo->mNames.SafeElementAt(i, empty));
+
+ nsTArray<RefPtr<nsAtom>> lineNames;
+ AddLineNamesIfNotPresent(lineNames, possiblyDuplicateLineNames);
+
+ // Add in names from grid areas where this line is used as a boundary.
+ for (auto area : aAreas) {
+ // We specifically ignore line names from implicitly named areas,
+ // because it can be confusing for designers who might naturally use
+ // a named line of "-start" or "-end" and create an implicit named
+ // area without meaning to.
+ if (area->Type() == GridDeclaration::Implicit) {
+ continue;
+ }
+
+ bool haveNameToAdd = false;
+ nsAutoString nameToAdd;
+ area->GetName(nameToAdd);
+ if (aIsRow) {
+ if (area->RowStart() == line1Index) {
+ haveNameToAdd = true;
+ nameToAdd.AppendLiteral("-start");
+ } else if (area->RowEnd() == line1Index) {
+ haveNameToAdd = true;
+ nameToAdd.AppendLiteral("-end");
+ }
+ } else {
+ if (area->ColumnStart() == line1Index) {
+ haveNameToAdd = true;
+ nameToAdd.AppendLiteral("-start");
+ } else if (area->ColumnEnd() == line1Index) {
+ haveNameToAdd = true;
+ nameToAdd.AppendLiteral("-end");
+ }
+ }
+
+ if (haveNameToAdd) {
+ RefPtr<nsAtom> name = NS_Atomize(nameToAdd);
+ AddLineNameIfNotPresent(lineNames, name);
+ }
+ }
+
+ if (i >= (aTrackInfo->mRepeatFirstTrack +
+ aTrackInfo->mNumLeadingImplicitTracks) &&
+ repeatIndex < numRepeatTracks) {
+ numAddedLines += AppendRemovedAutoFits(
+ aTrackInfo, aLineInfo, lastTrackEdge, repeatIndex, numRepeatTracks,
+ leadingTrackCount, lineNames);
+ }
+
+ // If this line is the one that ends a repeat, then add
+ // in the mNamesFollowingRepeat names from aLineInfo.
+ if (numRepeatTracks > 0 && i == (aTrackInfo->mRepeatFirstTrack +
+ aTrackInfo->mNumLeadingImplicitTracks +
+ numRepeatTracks - numAddedLines)) {
+ AddLineNamesIfNotPresent(lineNames, aLineInfo->mNamesFollowingRepeat);
+ }
+
+ RefPtr<GridLine> line = new GridLine(this);
+ mLines.AppendElement(line);
+ MOZ_ASSERT(line1Index > 0, "line1Index must be positive.");
+ bool isBeforeFirstExplicit =
+ (line1Index <= aTrackInfo->mNumLeadingImplicitTracks);
+ bool isAfterLastExplicit = line1Index > (leadingTrackCount + 1);
+ // Calculate an actionable line number for this line, that could be used
+ // in a css grid property to align a grid item or area at that line.
+ // For implicit lines that appear before line 1, report a number of 0.
+ // We can't report negative indexes, because those have a different
+ // meaning in the css grid spec (negative indexes are negative-1-based
+ // from the end of the grid decreasing towards the front).
+ uint32_t lineNumber = isBeforeFirstExplicit
+ ? 0
+ : (line1Index + numAddedLines -
+ aTrackInfo->mNumLeadingImplicitTracks);
+
+ // The negativeNumber is counted back from the leadingTrackCount.
+ int32_t lineNegativeNumber =
+ isAfterLastExplicit
+ ? 0
+ : (line1Index + numAddedLines - (leadingTrackCount + 2));
+ GridDeclaration lineType = (isBeforeFirstExplicit || isAfterLastExplicit)
+ ? GridDeclaration::Implicit
+ : GridDeclaration::Explicit;
+ line->SetLineValues(
+ lineNames, nsPresContext::AppUnitsToDoubleCSSPixels(lastTrackEdge),
+ nsPresContext::AppUnitsToDoubleCSSPixels(startOfNextTrack -
+ lastTrackEdge),
+ lineNumber, lineNegativeNumber, lineType);
+
+ if (i < aTrackInfo->mEndFragmentTrack) {
+ lastTrackEdge = aTrackInfo->mPositions[i] + aTrackInfo->mSizes[i];
+ }
+ }
+
+ // Define a function that gets the mLines index for a given line number.
+ // This is necessary since it's possible for a line number to not be
+ // represented in mLines. If this is the case, then return -1.
+ const int32_t lineCount = mLines.Length();
+ const uint32_t lastLineNumber = mLines[lineCount - 1]->Number();
+ auto IndexForLineNumber =
+ [lineCount, lastLineNumber](uint32_t aLineNumber) -> int32_t {
+ if (lastLineNumber == 0) {
+ // None of the lines have addressable numbers, so none of them can have
+ // aLineNumber
+ return -1;
+ }
+
+ int32_t possibleIndex = (int32_t)aLineNumber - 1;
+ if (possibleIndex < 0 || possibleIndex > lineCount - 1) {
+ // aLineNumber is not represented in mLines.
+ return -1;
+ }
+
+ return possibleIndex;
+ };
+
+ // Post-processing loop for implicit grid areas.
+ for (const auto& area : aAreas) {
+ if (area->Type() == GridDeclaration::Implicit) {
+ // Get the appropriate indexes for the area's start and end lines as
+ // they are represented in mLines.
+ int32_t startIndex =
+ IndexForLineNumber(aIsRow ? area->RowStart() : area->ColumnStart());
+ int32_t endIndex =
+ IndexForLineNumber(aIsRow ? area->RowEnd() : area->ColumnEnd());
+
+ // If both start and end indexes are -1, then stop here since we cannot
+ // reason about the naming for either lines.
+ if (startIndex < 0 && endIndex < 0) {
+ break;
+ }
+
+ // Get the "-start" and "-end" line names of the grid area.
+ nsAutoString startLineName;
+ area->GetName(startLineName);
+ startLineName.AppendLiteral("-start");
+ nsAutoString endLineName;
+ area->GetName(endLineName);
+ endLineName.AppendLiteral("-end");
+
+ // Get the list of existing line names for the start and end of the grid
+ // area. In the case where one of the start or end indexes are -1, use a
+ // dummy line as a substitute for the start/end line.
+ RefPtr<GridLine> dummyLine = new GridLine(this);
+ RefPtr<GridLine> areaStartLine =
+ startIndex > -1 ? mLines[startIndex] : dummyLine;
+ nsTArray<RefPtr<nsAtom>> startLineNames(areaStartLine->Names().Clone());
+
+ RefPtr<GridLine> areaEndLine =
+ endIndex > -1 ? mLines[endIndex] : dummyLine;
+ nsTArray<RefPtr<nsAtom>> endLineNames(areaEndLine->Names().Clone());
+
+ RefPtr<nsAtom> start = NS_Atomize(startLineName);
+ RefPtr<nsAtom> end = NS_Atomize(endLineName);
+ if (startLineNames.Contains(end) || endLineNames.Contains(start)) {
+ // Add the reversed line names.
+ AddLineNameIfNotPresent(startLineNames, end);
+ AddLineNameIfNotPresent(endLineNames, start);
+ } else {
+ // Add the normal line names.
+ AddLineNameIfNotPresent(startLineNames, start);
+ AddLineNameIfNotPresent(endLineNames, end);
+ }
+
+ areaStartLine->SetLineNames(startLineNames);
+ areaEndLine->SetLineNames(endLineNames);
+ }
+ }
+ }
+}
+
+uint32_t GridLines::AppendRemovedAutoFits(
+ const ComputedGridTrackInfo* aTrackInfo,
+ const ComputedGridLineInfo* aLineInfo, nscoord aLastTrackEdge,
+ uint32_t& aRepeatIndex, uint32_t aNumRepeatTracks,
+ uint32_t aNumLeadingTracks, nsTArray<RefPtr<nsAtom>>& aLineNames) {
+ bool extractedExplicitLineNames = false;
+ nsTArray<RefPtr<nsAtom>> explicitLineNames;
+ uint32_t linesAdded = 0;
+ while (aRepeatIndex < aNumRepeatTracks &&
+ aTrackInfo->mRemovedRepeatTracks[aRepeatIndex]) {
+ // If this is not the very first call to this function, and if we
+ // haven't already added a line this call, pull all the explicit
+ // names to pass along to the next line that will be added after
+ // this function completes.
+ if (aRepeatIndex > 0 && linesAdded == 0) {
+ // Find the names that didn't match the before or after names,
+ // and extract them.
+ for (const auto& name : aLineNames) {
+ if (!aLineInfo->mNamesBefore.Contains(name) &&
+ !aLineInfo->mNamesAfter.Contains(name)) {
+ explicitLineNames.AppendElement(name);
+ }
+ }
+ for (const auto& extractedName : explicitLineNames) {
+ aLineNames.RemoveElement(extractedName);
+ }
+ extractedExplicitLineNames = true;
+ }
+
+ AddLineNamesIfNotPresent(aLineNames, aLineInfo->mNamesBefore);
+
+ RefPtr<GridLine> line = new GridLine(this);
+ mLines.AppendElement(line);
+
+ // Time to calculate the line numbers. For the positive numbers
+ // we count with a 1-based index from mRepeatFirstTrack. Although
+ // this number is the index of the first repeat track AFTER all
+ // the leading implicit tracks, that's still what we want since
+ // all those leading implicit tracks have line number 0.
+ uint32_t lineNumber = aTrackInfo->mRepeatFirstTrack + aRepeatIndex + 1;
+
+ // The negative number does have to account for the leading
+ // implicit tracks. We've been passed aNumLeadingTracks which is
+ // the total of the leading implicit tracks plus the explicit
+ // tracks. So all we have to do is subtract that number plus one
+ // from the 0-based index of this track.
+ int32_t lineNegativeNumber =
+ (aTrackInfo->mNumLeadingImplicitTracks + aTrackInfo->mRepeatFirstTrack +
+ aRepeatIndex) -
+ (aNumLeadingTracks + 1);
+ line->SetLineValues(
+ aLineNames, nsPresContext::AppUnitsToDoubleCSSPixels(aLastTrackEdge),
+ nsPresContext::AppUnitsToDoubleCSSPixels(0), lineNumber,
+ lineNegativeNumber, GridDeclaration::Explicit);
+
+ // No matter what, the next line should have the after names associated
+ // with it. If we go through the loop again, the before names will also
+ // be added.
+ aLineNames = aLineInfo->mNamesAfter.Clone();
+ aRepeatIndex++;
+
+ linesAdded++;
+ }
+
+ aRepeatIndex++;
+
+ if (extractedExplicitLineNames) {
+ // Pass on the explicit names we saved to the next explicit line.
+ AddLineNamesIfNotPresent(aLineNames, explicitLineNames);
+ }
+
+ // If we haven't finished adding auto-repeated tracks, then we need to put
+ // back the before names, in case we cleared them above.
+ if (aRepeatIndex < aNumRepeatTracks) {
+ AddLineNamesIfNotPresent(aLineNames, aLineInfo->mNamesBefore);
+ }
+
+ return linesAdded;
+}
+
+} // namespace mozilla::dom
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..d90a421317
--- /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.toml"]
+
+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.toml b/dom/grid/test/chrome.toml
new file mode 100644
index 0000000000..47aba99351
--- /dev/null
+++ b/dom/grid/test/chrome.toml
@@ -0,0 +1,23 @@
+[DEFAULT]
+
+["chrome/test_grid_areas.html"]
+
+["chrome/test_grid_equality.html"]
+
+["chrome/test_grid_fragmentation.html"]
+
+["chrome/test_grid_implicit.html"]
+
+["chrome/test_grid_line_numbers.html"]
+
+["chrome/test_grid_lines.html"]
+
+["chrome/test_grid_object.html"]
+
+["chrome/test_grid_repeat_auto_fill.html"]
+
+["chrome/test_grid_repeat_auto_fit.html"]
+
+["chrome/test_grid_subtree_dirty.html"]
+
+["chrome/test_grid_tracks.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_equality.html b/dom/grid/test/chrome/test_grid_equality.html
new file mode 100644
index 0000000000..741f54d016
--- /dev/null
+++ b/dom/grid/test/chrome/test_grid_equality.html
@@ -0,0 +1,57 @@
+<!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;
+ }
+
+ .cellOriginal {
+ grid-column-end: span 2;
+ }
+
+ .cellChanged {
+ grid-column-end: span 5;
+ }
+</style>
+<script>
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ const grid = document.getElementById("grid");
+ const cell = document.getElementById("cell");
+
+ const gridA = grid.getGridFragments()[0];
+
+ // Check that grid fragments are equal across reflows.
+ document.documentElement.offsetHeight; // Flush layout.
+ const gridB = grid.getGridFragments()[0];
+ is(gridB, gridA, "Grid fragments should be the same across reflows.");
+
+ // Check that grid fragments are different when styles are different.
+ cell.classList.replace("cellOriginal", "cellChanged");
+ const gridC = grid.getGridFragments()[0];
+ isnot(gridC, gridA, "Grids with different styles should have different fragments.");
+
+ // Check that changing a grid to a matching style results in different fragments.
+ cell.classList.replace("cellChanged", "cellOriginal");
+ const gridD = grid.getGridFragments()[0];
+ isnot(gridD, gridA, "Grids changed to the same styles should have different fragments.");
+
+ SimpleTest.finish();
+}
+</script>
+</head>
+<body onLoad="runTests();">
+<div id="grid">
+ <div id="cell" class="cellOriginal"></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>