Bug 1297189 - Expose implicit grid areas via dev tools API. r=mats, r=bz
authorBrad Werth <bwerth@mozilla.com>
Fri, 02 Sep 2016 16:28:27 -0700
changeset 312939 cd249dba87c2660f93409a99ca9296386d230345
parent 312938 ba853a36a9737bd58f90a042a1bc56f55938a21f
child 312940 f25b22784bc95fbe57db5819995d733a980ec213
push id30665
push usercbook@mozilla.com
push dateWed, 07 Sep 2016 15:20:43 +0000
treeherdermozilla-central@95acb9299faf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmats, bz
bugs1297189
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1297189 - Expose implicit grid areas via dev tools API. r=mats, r=bz
dom/grid/Grid.cpp
dom/grid/GridDimension.cpp
dom/grid/GridDimension.h
dom/grid/GridLines.cpp
dom/grid/GridLines.h
dom/grid/test/chrome/test_grid_areas.html
dom/grid/test/chrome/test_grid_implicit.html
dom/webidl/Grid.webidl
layout/generic/nsGridContainerFrame.cpp
layout/generic/nsGridContainerFrame.h
--- a/dom/grid/Grid.cpp
+++ b/dom/grid/Grid.cpp
@@ -26,63 +26,73 @@ Grid::Grid(nsISupports* aParent,
            nsGridContainerFrame* aFrame)
   : mParent(do_QueryInterface(aParent))
   , mRows(new GridDimension(this))
   , mCols(new GridDimension(this))
 {
   MOZ_ASSERT(aFrame,
     "Should never be instantiated with a null nsGridContainerFrame");
 
-  const ComputedGridTrackInfo* rowTrackInfo =
-    aFrame->GetComputedTemplateRows();
-  const ComputedGridLineInfo* rowLineInfo =
-    aFrame->GetComputedTemplateRowLines();
-  mRows->SetTrackInfo(rowTrackInfo);
-  mRows->SetLineInfo(rowTrackInfo, rowLineInfo);
+  // Construct areas first, because lines may need to reference them
+  // to extract additional names for boundary lines.
 
-  const ComputedGridTrackInfo* columnTrackInfo =
-    aFrame->GetComputedTemplateColumns();
-  const ComputedGridLineInfo* columnLineInfo =
-    aFrame->GetComputedTemplateColumnLines();
-  mCols->SetTrackInfo(columnTrackInfo);
-  mCols->SetLineInfo(columnTrackInfo, columnLineInfo);
-
-  // Add implicit areas first.
+  // Add implicit areas first. Track the names that we add here, because
+  // we will ignore future explicit areas with the same name.
+  nsTHashtable<nsStringHashKey> namesSeen;
   nsGridContainerFrame::ImplicitNamedAreas* implicitAreas =
     aFrame->GetImplicitNamedAreas();
   if (implicitAreas) {
     for (auto iter = implicitAreas->Iter(); !iter.Done(); iter.Next()) {
-      nsStringHashKey* entry = iter.Get();
-
-      GridArea* area = new GridArea(this,
-                                    nsString(entry->GetKey()),
-                                    GridDeclaration::Implicit,
-                                    0,
-                                    0,
-                                    0,
-                                    0);
-      mAreas.AppendElement(area);
-    }
-  }
-
-  // Add explicit areas next.
-  nsGridContainerFrame::ExplicitNamedAreas* explicitAreas =
-    aFrame->GetExplicitNamedAreas();
-  if (explicitAreas) {
-    for (auto areaInfo : *explicitAreas) {
+      auto& areaInfo = iter.Data();
+      namesSeen.PutEntry(areaInfo.mName);
       GridArea* area = new GridArea(this,
                                     areaInfo.mName,
-                                    GridDeclaration::Explicit,
+                                    GridDeclaration::Implicit,
                                     areaInfo.mRowStart,
                                     areaInfo.mRowEnd,
                                     areaInfo.mColumnStart,
                                     areaInfo.mColumnEnd);
       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.
+  nsGridContainerFrame::ExplicitNamedAreas* explicitAreas =
+    aFrame->GetExplicitNamedAreas();
+  if (explicitAreas) {
+    for (auto& areaInfo : *explicitAreas) {
+      if (!namesSeen.Contains(areaInfo.mName)) {
+        GridArea* area = new GridArea(this,
+                                      areaInfo.mName,
+                                      GridDeclaration::Explicit,
+                                      areaInfo.mRowStart,
+                                      areaInfo.mRowEnd,
+                                      areaInfo.mColumnStart,
+                                      areaInfo.mColumnEnd);
+        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()
 {
 }
 
 JSObject*
 Grid::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
--- a/dom/grid/GridDimension.cpp
+++ b/dom/grid/GridDimension.cpp
@@ -56,15 +56,17 @@ GridDimension::Tracks() const
 void
 GridDimension::SetTrackInfo(const ComputedGridTrackInfo* aTrackInfo)
 {
   mTracks->SetTrackInfo(aTrackInfo);
 }
 
 void
 GridDimension::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo,
-                           const ComputedGridLineInfo* aLineInfo)
+                           const ComputedGridLineInfo* aLineInfo,
+                           const nsTArray<RefPtr<GridArea>>& aAreas,
+                           bool aIsRow)
 {
-  mLines->SetLineInfo(aTrackInfo, aLineInfo);
+  mLines->SetLineInfo(aTrackInfo, aLineInfo, aAreas, aIsRow);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/grid/GridDimension.h
+++ b/dom/grid/GridDimension.h
@@ -38,17 +38,19 @@ public:
     return mParent;
   }
 
   GridLines* Lines() const;
   GridTracks* Tracks() const;
 
   void SetTrackInfo(const ComputedGridTrackInfo* aTrackInfo);
   void SetLineInfo(const ComputedGridTrackInfo* aTrackInfo,
-                   const ComputedGridLineInfo* aLineInfo);
+                   const ComputedGridLineInfo* aLineInfo,
+                   const nsTArray<RefPtr<GridArea>>& aAreas,
+                   bool aIsRow);
 
 protected:
   RefPtr<Grid> mParent;
   RefPtr<GridLines> mLines;
   RefPtr<GridTracks> mTracks;
 };
 
 } // namespace dom
--- a/dom/grid/GridLines.cpp
+++ b/dom/grid/GridLines.cpp
@@ -59,17 +59,19 @@ GridLines::IndexedGetter(uint32_t aIndex
   if (!aFound) {
     return nullptr;
   }
   return mLines[aIndex];
 }
 
 void
 GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo,
-                       const ComputedGridLineInfo* aLineInfo)
+                       const ComputedGridLineInfo* aLineInfo,
+                       const nsTArray<RefPtr<GridArea>>& aAreas,
+                       bool aIsRow)
 {
   mLines.Clear();
 
   if (!aTrackInfo) {
     return;
   }
 
   uint32_t trackCount = aTrackInfo->mEndFragmentTrack -
@@ -79,34 +81,64 @@ GridLines::SetLineInfo(const ComputedGri
   // than the number of tracks.
   if (trackCount > 0) {
     double endOfLastTrack = 0.0;
     double startOfNextTrack;
 
     for (uint32_t i = aTrackInfo->mStartFragmentTrack;
          i < aTrackInfo->mEndFragmentTrack + 1;
          i++) {
+      uint32_t line1Index = i + 1;
+
       startOfNextTrack = (i < aTrackInfo->mEndFragmentTrack) ?
                          aTrackInfo->mPositions[i] :
                          endOfLastTrack;
 
       GridLine* line = new GridLine(this);
       mLines.AppendElement(line);
 
       nsTArray<nsString> lineNames;
       if (aLineInfo) {
         lineNames = aLineInfo->mNames.SafeElementAt(i, nsTArray<nsString>());
       }
 
+      // Add in names from grid areas where this line is used as a boundary.
+      for (auto area : aAreas) {
+        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 && !lineNames.Contains(nameToAdd)) {
+          lineNames.AppendElement(nameToAdd);
+        }
+      }
+
       line->SetLineValues(
         lineNames,
         nsPresContext::AppUnitsToDoubleCSSPixels(endOfLastTrack),
         nsPresContext::AppUnitsToDoubleCSSPixels(startOfNextTrack -
                                                  endOfLastTrack),
-        i + 1,
+        line1Index,
         (
           // Implicit if there are no explicit tracks, or if the index
           // is before the first explicit track, or after
           // a track beyond the last explicit track.
           (aTrackInfo->mNumExplicitTracks == 0) ||
           (i < aTrackInfo->mNumLeadingImplicitTracks) ||
           (i > aTrackInfo->mNumLeadingImplicitTracks +
                aTrackInfo->mNumExplicitTracks) ?
--- a/dom/grid/GridLines.h
+++ b/dom/grid/GridLines.h
@@ -35,17 +35,19 @@ public:
     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 ComputedGridLineInfo* aLineInfo,
+                   const nsTArray<RefPtr<GridArea>>& aAreas,
+                   bool aIsRow);
 
 protected:
   RefPtr<GridDimension> mParent;
   nsTArray<RefPtr<GridLine>> mLines;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/grid/test/chrome/test_grid_areas.html
+++ b/dom/grid/test/chrome/test_grid_areas.html
@@ -57,31 +57,78 @@ function runTests() {
 			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 start and end lines
+			// 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();">
--- a/dom/grid/test/chrome/test_grid_implicit.html
+++ b/dom/grid/test/chrome/test_grid_implicit.html
@@ -6,124 +6,245 @@
 <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-template-columns: 100px 50px 100px;
-	grid-template-rows: 50px [areaD-start] 50px [areaD-end];
-	grid-template-areas: "areaA areaA ....."
-						 "..... areaC areaC";
 	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];
+}
+
 .box {
 	background-color: #444;
 	color: #fff;
 }
 .a {
 	grid-area: areaA;
 }
 .b {
-	grid-row: span 2 / 2;
+	grid-row: span got-this-name-implicitly / 2;
 	grid-column: areaA-end / span 2;
 }
 .c {
 	grid-area: areaC;
 }
 .d {
 	grid-area: areaD;
 }
 </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");
-	var boxC = document.getElementById("boxC");
-
+	// 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)) {
+	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 1 is implicit.");
 		is(grid.rows.lines[1].type, "explicit", "Grid row line 2 is explicit.");
 		is(grid.rows.lines[3].type, "explicit", "Grid row line 4 is explicit.");
+		
+		// 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)) {
+	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(var i = 0; i < grid.areas.length; i++) {
+	for (var i = 0; i < grid.areas.length; i++) {
 		var area = grid.areas[i];
-		if(area.name == "areaD") {
+		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 (var i = 0; i < grid.areas.length; i++) {
+		var area = grid.areas[i];
+		if (area.name == "areaA") {
 			is(area.type, "implicit", area.name + " is implicit.");
 
 			// test lines of implicit areas
-			todo_is(area.rowStart, 2, area.name + " has start row line of 2.");
-			todo_is(area.rowEnd, 3, area.name + " has end row line of 3.");
-			todo_is(area.columnStart, 4, area.name + " has start column line of 4.");
-			todo_is(area.columnEnd, 5, area.name + " has end column line of 5.");
-		} else {
-			is(area.type, "explicit", area.name + " is explicit.");
+			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 both the explicit line names and the implicit line names
+		isnot(grid.cols.lines[0].names.indexOf("areaC-start"), -1,
+			"Grid row line 1 has 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'."
+		);
+		
+		isnot(grid.cols.lines[1].names.indexOf("areaC-end"), -1,
+			"Grid row line 2 has the name 'areaC-end'."
+		);
+	}
+	
+	// test area count
+	is(grid.areas.length, 4,
+		"Grid.areas property reports 4 areas."
+	);
+	
+	for (var i = 0; i < grid.areas.length; i++) {
+		var 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.");
 		}
 	}
 
 	SimpleTest.finish();
 }
 </script>
 </head>
 <body onLoad="runTests();">
 
-	<div id="wrapper" class="wrapper">
+	<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>
 
 </body>
 </html>
--- a/dom/webidl/Grid.webidl
+++ b/dom/webidl/Grid.webidl
@@ -100,16 +100,14 @@ interface GridTrack
 [ChromeOnly]
 interface GridArea
 {
   readonly attribute DOMString name;
   readonly attribute GridDeclaration type;
 
   /**
    * These values are 1-indexed line numbers bounding the area.
-   * FIXME: Bug 1297189 - Implicit grid areas need boundary line numbers
-   * exposed to dev tools
    */
   readonly attribute unsigned long rowStart;
   readonly attribute unsigned long rowEnd;
   readonly attribute unsigned long columnStart;
   readonly attribute unsigned long columnEnd;
 };
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -1623,17 +1623,17 @@ struct nsGridContainerFrame::Tracks
   {
     MOZ_ASSERT(mCanResolveLineRangeSize);
     MOZ_ASSERT(aRange.Extent() > 0, "grid items cover at least one track");
     nscoord pos, size;
     aRange.ToPositionAndLength(mSizes, &pos, &size);
     return size;
   }
 
-  nsTArray<nsString> GetLineNamesAtIndex(
+  nsTArray<nsString> GetExplicitLineNamesAtIndex(
     const nsStyleGridTemplate& aGridTemplate,
     const TrackSizingFunctions& aFunctions,
     uint32_t aIndex)
   {
     nsTArray<nsString> lineNames;
 
     bool hasRepeatAuto = aGridTemplate.HasRepeatAuto();
     const nsTArray<nsTArray<nsString>>& lineNameLists(
@@ -2310,17 +2310,16 @@ struct MOZ_STACK_CLASS nsGridContainerFr
    * Offsets from the start of the implicit grid to the start of the translated
    * explicit grid.  They are zero if there are no implicit lines before 1,1.
    * e.g. "grid-column: span 3 / 1" makes mExplicitGridOffsetCol = 3 and the
    * corresponding GridArea::mCols will be 0 / 3 in the zero-based translated
    * grid.
    */
   uint32_t mExplicitGridOffsetCol;
   uint32_t mExplicitGridOffsetRow;
-
 };
 
 void
 nsGridContainerFrame::GridReflowInput::CalculateTrackSizes(
   const Grid&        aGrid,
   const LogicalSize& aContentBox,
   IntrinsicISizeType aConstraint)
 {
@@ -2673,25 +2672,40 @@ nsGridContainerFrame::AddImplicitNamedAr
   // http://dev.w3.org/csswg/css-grid/#implicit-named-areas
   // Note: recording these names for fast lookup later is just an optimization.
   const uint32_t len =
     std::min(aLineNameLists.Length(), size_t(nsStyleGridLine::kMaxLine));
   nsTHashtable<nsStringHashKey> currentStarts;
   ImplicitNamedAreas* areas = GetImplicitNamedAreas();
   for (uint32_t i = 0; i < len; ++i) {
     for (const nsString& name : aLineNameLists[i]) {
-      uint32_t index;
-      if (Grid::IsNameWithStartSuffix(name, &index) ||
-          Grid::IsNameWithEndSuffix(name, &index)) {
-        nsDependentSubstring area(name, 0, index);
+      uint32_t indexOfSuffix;
+      if (Grid::IsNameWithStartSuffix(name, &indexOfSuffix) ||
+          Grid::IsNameWithEndSuffix(name, &indexOfSuffix)) {
+        // Extract the name that was found earlier.
+        nsDependentSubstring areaName(name, 0, indexOfSuffix);
+
+        // Lazily create the ImplicitNamedAreas.
         if (!areas) {
           areas = new ImplicitNamedAreas;
           Properties().Set(ImplicitNamedAreasProperty(), areas);
         }
-        areas->PutEntry(area);
+
+        mozilla::css::GridNamedArea area;
+        if (!areas->Get(areaName, &area)) {
+          // Not found, so prep the newly-seen area with a name and empty
+          // boundary information, which will get filled in later.
+          area.mName = areaName;
+          area.mRowStart = 0;
+          area.mRowEnd = 0;
+          area.mColumnStart = 0;
+          area.mColumnEnd = 0;
+
+          areas->Put(areaName, area);
+        }
       }
     }
   }
 }
 
 void
 nsGridContainerFrame::InitImplicitNamedAreas(const nsStylePosition* aStyle)
 {
@@ -3452,16 +3466,48 @@ nsGridContainerFrame::Grid::PlaceGridIte
     mGridRowEnd -= numEmptyRows;
     mExplicitGridRowEnd -= numEmptyRows;
     // Adjust the track mapping to unmap the removed tracks.
     auto finalColRepeatCount = aState.mColFunctions.NumRepeatTracks() - numEmptyCols;
     aState.mColFunctions.SetNumRepeatTracks(finalColRepeatCount);
     auto finalRowRepeatCount = aState.mRowFunctions.NumRepeatTracks() - numEmptyRows;
     aState.mRowFunctions.SetNumRepeatTracks(finalRowRepeatCount);
   }
+
+  // Update the line boundaries of the implicit grid areas, if needed.
+  if (mAreas &&
+      aState.mFrame->HasAnyStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES)) {
+    for (auto iter = mAreas->Iter(); !iter.Done(); iter.Next()) {
+      auto& areaInfo = iter.Data();
+
+      // Resolve the lines for the area. We use the name of the area as the
+      // name of the lines, knowing that the line placement algorithm will
+      // add the -start and -end suffixes as appropriate for layout.
+      nsStyleGridLine lineStartAndEnd;
+      lineStartAndEnd.mLineName = areaInfo.mName;
+
+      LineRange columnLines = ResolveLineRange(
+        lineStartAndEnd, lineStartAndEnd,
+        colLineNameMap,
+        &GridNamedArea::mColumnStart, &GridNamedArea::mColumnEnd,
+        mExplicitGridColEnd, gridStyle);
+
+      LineRange rowLines = ResolveLineRange(
+        lineStartAndEnd, lineStartAndEnd,
+        rowLineNameMap,
+        &GridNamedArea::mRowStart, &GridNamedArea::mRowEnd,
+        mExplicitGridRowEnd, gridStyle);
+
+      // Put the resolved line indices back into the area structure.
+      areaInfo.mColumnStart = columnLines.mStart + mExplicitGridOffsetCol;
+      areaInfo.mColumnEnd = columnLines.mEnd + mExplicitGridOffsetCol;
+      areaInfo.mRowStart = rowLines.mStart + mExplicitGridOffsetRow;
+      areaInfo.mRowEnd = rowLines.mEnd + mExplicitGridOffsetRow;
+    }
+  }
 }
 
 void
 nsGridContainerFrame::Tracks::Initialize(
   const TrackSizingFunctions& aFunctions,
   const nsStyleCoord&         aGridGap,
   uint32_t                    aNumTracks,
   nscoord                     aContentBoxSize)
@@ -5804,43 +5850,51 @@ nsGridContainerFrame::Reflow(nsPresConte
         gridReflowInput.mStartRow,
         Move(priorRowInfo->mPositions),
         Move(priorRowInfo->mSizes),
         Move(priorRowInfo->mStates));
       prevInFlow->Properties().Set(GridRowTrackInfo(), revisedPriorRowInfo);
     }
 
     // Generate the line info properties. We need to provide the number of
-    // repeat tracks produced in the reflow.
+    // repeat tracks produced in the reflow. Only explicit names are assigned
+    // to lines here; the mozilla::dom::GridLines class will later extract
+    // implicit names from grid areas and assign them to the appropriate lines.
 
     // Generate column lines first.
     uint32_t capacity = gridReflowInput.mColFunctions.NumRepeatTracks() +
                         gridReflowInput.mCols.mSizes.Length();
     nsTArray<nsTArray<nsString>> columnLineNames(capacity);
     for (col = 0; col <= gridReflowInput.mCols.mSizes.Length(); col++) {
-      columnLineNames.AppendElement(
-        gridReflowInput.mCols.GetLineNamesAtIndex(
+      // Offset col by the explicit grid offset, to get the original names.
+      nsTArray<nsString> explicitNames =
+        gridReflowInput.mCols.GetExplicitLineNamesAtIndex(
           gridReflowInput.mGridStyle->mGridTemplateColumns,
           gridReflowInput.mColFunctions,
-          col));
+          col - gridReflowInput.mColFunctions.mExplicitGridOffset);
+
+      columnLineNames.AppendElement(explicitNames);
     }
     ComputedGridLineInfo* columnLineInfo = new ComputedGridLineInfo(
       Move(columnLineNames));
     Properties().Set(GridColumnLineInfo(), columnLineInfo);
 
     // Generate row lines next.
     capacity = gridReflowInput.mRowFunctions.NumRepeatTracks() +
                gridReflowInput.mRows.mSizes.Length();
     nsTArray<nsTArray<nsString>> rowLineNames(capacity);
     for (row = 0; row <= gridReflowInput.mRows.mSizes.Length(); row++) {
-      rowLineNames.AppendElement(
-        gridReflowInput.mRows.GetLineNamesAtIndex(
+      // Offset row by the explicit grid offset, to get the original names.
+      nsTArray<nsString> explicitNames =
+        gridReflowInput.mRows.GetExplicitLineNamesAtIndex(
           gridReflowInput.mGridStyle->mGridTemplateRows,
           gridReflowInput.mRowFunctions,
-          row));
+          row - gridReflowInput.mRowFunctions.mExplicitGridOffset);
+
+      rowLineNames.AppendElement(explicitNames);
     }
     ComputedGridLineInfo* rowLineInfo = new ComputedGridLineInfo(
       Move(rowLineNames));
     Properties().Set(GridRowLineInfo(), rowLineInfo);
 
     // Generate area info for explicit areas. Implicit areas are handled
     // elsewhere.
     if (gridReflowInput.mGridStyle->mGridTemplateAreas) {
--- a/layout/generic/nsGridContainerFrame.h
+++ b/layout/generic/nsGridContainerFrame.h
@@ -146,17 +146,19 @@ public:
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridRowLineInfo, ComputedGridLineInfo)
   const ComputedGridLineInfo* GetComputedTemplateRowLines()
   {
     const ComputedGridLineInfo* info = Properties().Get(GridRowLineInfo());
     MOZ_ASSERT(info, "Property generation wasn't requested.");
     return info;
   }
 
-  typedef nsTHashtable<nsStringHashKey> ImplicitNamedAreas;
+  typedef nsBaseHashtable<nsStringHashKey,
+                          mozilla::css::GridNamedArea,
+                          mozilla::css::GridNamedArea> ImplicitNamedAreas;
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(ImplicitNamedAreasProperty,
                                       ImplicitNamedAreas)
   ImplicitNamedAreas* GetImplicitNamedAreas() const {
     return Properties().Get(ImplicitNamedAreasProperty());
   }
 
   typedef nsTArray<mozilla::css::GridNamedArea> ExplicitNamedAreas;
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(ExplicitNamedAreasProperty,