[CSS Grid Layout] Fix positioning grid items using named grid lines/areas
authorsvillar@igalia.com <svillar@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 28 Feb 2014 18:34:48 +0000 (18:34 +0000)
committersvillar@igalia.com <svillar@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 28 Feb 2014 18:34:48 +0000 (18:34 +0000)
https://bugs.webkit.org/show_bug.cgi?id=129372

Reviewed by Darin Adler.

Source/WebCore:

Our code was assuming that a <custom-ident> in
-webkit-grid-{column|row}-{start|end} and
-webkit-grid-{column|row} was always a grid area name. That's
wrong because the <custom-ident> could be also a explicitly named
grid line or the an implicitly named grid line created by a grid
area definition.

The style resolution code was not correct either. This patch fixes
it so it now matches the spec, which means that:
- first we try to match any existing grid area.
- then if there is a named grid line with the name
<custom-ident>-{start|end} for -webkit-grid-{column|row}-{start|end}
defined before the grid area then we use it instead of the grid
area.
- otherwise if there is a named grid line we resolve to the first such line.
- otherwise we treat it as 'auto'.

Fixing this uncovered a bug in GridPosition, we were not using the
name of the stored grid area to check if two GridPositions were
the same.

Tests: fast/css-grid-layout/grid-item-position-changed-dynamic.html
       fast/css-grid-layout/named-grid-lines-with-named-grid-areas-resolution.html

* css/StyleResolver.cpp:
(WebCore::gridLineDefinedBeforeGridArea): New function to check if
a given named grid line was defined before an implicit named grid
line created by a grid area definition.
(WebCore::StyleResolver::adjustNamedGridItemPosition): New
function that adjusts the position of a GridPosition parsed as a
grid area.
(WebCore::StyleResolver::adjustGridItemPosition): Use the new
function adjustNamedGridItemPosition to adjust the positions of
named grid lines.
* css/StyleResolver.h:
* rendering/RenderGrid.cpp:
(WebCore::RenderGrid::resolveNamedGridLinePositionFromStyle): Use GridPosition:: namespace.
(WebCore::RenderGrid::resolveGridPositionFromStyle): Ditto.
(WebCore::RenderGrid::resolveRowEndColumnEndNamedGridLinePositionAgainstOppositePosition): Ditto.
* rendering/RenderGrid.h:
* rendering/style/GridPosition.h:
(WebCore::GridPosition::adjustGridPositionForRowEndColumnEndSide): Moved from RenderGrid.cpp.
(WebCore::GridPosition::adjustGridPositionForSide): Ditto.
(WebCore::GridPosition::operator==): Use the named grid line to check equality.

LayoutTests:

Added a new test that checks that we correctly position grid items
using named grid lines, grid areas and also with the implicit
named grid lines created by grid areas.

I'm also importing a test from Blink that checks that we can
dynamically change the position of a grid item by changing the
name of the grid lines used to position it.

* fast/css-grid-layout/grid-item-position-changed-dynamic-expected.txt:
Merged from Blink r153913 by <jchaffraix@chromium.org>.
* fast/css-grid-layout/grid-item-position-changed-dynamic.html: Ditto.
* fast/css-grid-layout/named-grid-lines-with-named-grid-areas-resolution-expected.txt: Added.
* fast/css-grid-layout/named-grid-lines-with-named-grid-areas-resolution.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@164869 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/css-grid-layout/grid-item-position-changed-dynamic-expected.txt [new file with mode: 0644]
LayoutTests/fast/css-grid-layout/grid-item-position-changed-dynamic.html [new file with mode: 0644]
LayoutTests/fast/css-grid-layout/named-grid-lines-with-named-grid-areas-resolution-expected.txt [new file with mode: 0644]
LayoutTests/fast/css-grid-layout/named-grid-lines-with-named-grid-areas-resolution.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/css/StyleResolver.cpp
Source/WebCore/css/StyleResolver.h
Source/WebCore/rendering/RenderGrid.cpp
Source/WebCore/rendering/RenderGrid.h
Source/WebCore/rendering/style/GridPosition.h

index 496dcac..d481345 100644 (file)
@@ -1,3 +1,24 @@
+2014-02-27  Sergio Villar Senin  <svillar@igalia.com>
+
+        [CSS Grid Layout] Fix positioning grid items using named grid lines/areas
+        https://bugs.webkit.org/show_bug.cgi?id=129372
+
+        Reviewed by Darin Adler.
+
+        Added a new test that checks that we correctly position grid items
+        using named grid lines, grid areas and also with the implicit
+        named grid lines created by grid areas.
+
+        I'm also importing a test from Blink that checks that we can
+        dynamically change the position of a grid item by changing the
+        name of the grid lines used to position it.
+
+        * fast/css-grid-layout/grid-item-position-changed-dynamic-expected.txt:
+        Merged from Blink r153913 by <jchaffraix@chromium.org>.
+        * fast/css-grid-layout/grid-item-position-changed-dynamic.html: Ditto.
+        * fast/css-grid-layout/named-grid-lines-with-named-grid-areas-resolution-expected.txt: Added.
+        * fast/css-grid-layout/named-grid-lines-with-named-grid-areas-resolution.html: Added.
+
 2014-02-28  Mario Sanchez Prada  <mario.prada@samsung.com>
 
         paragraphs with different directionality in textarea with unicode-bidi: plaintext are aligned the same
diff --git a/LayoutTests/fast/css-grid-layout/grid-item-position-changed-dynamic-expected.txt b/LayoutTests/fast/css-grid-layout/grid-item-position-changed-dynamic-expected.txt
new file mode 100644 (file)
index 0000000..2da2ed4
--- /dev/null
@@ -0,0 +1,90 @@
+This test checks that we properly recompute our internal grid when a grid item is moved.
+
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
diff --git a/LayoutTests/fast/css-grid-layout/grid-item-position-changed-dynamic.html b/LayoutTests/fast/css-grid-layout/grid-item-position-changed-dynamic.html
new file mode 100644 (file)
index 0000000..1623b2a
--- /dev/null
@@ -0,0 +1,190 @@
+<!DOCTYPE html>
+<html>
+<script>
+if (window.testRunner)
+    testRunner.overridePreference("WebKitCSSGridLayoutEnabled", 1);
+</script>
+<link href="resources/grid.css" rel="stylesheet">
+<style type="text/css">
+.grid {
+    -webkit-grid-template-columns: "col" 50px "col" 100px "col" 200px "col";
+    -webkit-grid-template-rows: "row" 70px "row" 140px "row" 280px "row";
+}
+.differentNamedGridLines {
+    -webkit-grid-template-columns: "col1" 50px "col2" 100px "col3" 200px "col4";
+    -webkit-grid-template-rows: "row1" 70px "row2" 140px "row3" 280px "row4";
+}
+</style>
+<script src="../../resources/check-layout.js"></script>
+<script>
+function testPosition(position, size)
+{
+    gridItem = document.getElementsByClassName("grid")[0].firstChild;
+    gridItem.style.webkitGridColumn = position.column;
+    gridItem.style.webkitGridRow = position.row;
+    gridItem.setAttribute("data-expected-width", size.width);
+    gridItem.setAttribute("data-expected-height", size.height);
+    checkLayout(".grid");
+}
+
+function updateGridItemPosition()
+{
+    // Test with the same type of positions.
+    // 1. Explicit grid lines.
+    testPosition({ 'column': '1 / 2', 'row': '1 / 2' }, { 'width': '50', 'height': '70' });
+    testPosition({ 'column': '1 / 3', 'row': '1 / 2' }, { 'width': '150', 'height': '70' });
+
+    testPosition({ 'column': '1 / 2', 'row': '1 / 3' }, { 'width': '50', 'height': '210' });
+    testPosition({ 'column': '1 / 2', 'row': '1 / 2' }, { 'width': '50', 'height': '70' });
+
+    testPosition({ 'column': '2 / 3', 'row': '1 / 3' }, { 'width': '100', 'height': '210' });
+    testPosition({ 'column': '1 / 3', 'row': '1 / 3' }, { 'width': '150', 'height': '210' });
+
+    testPosition({ 'column': '1 / 3', 'row': '1 / 3' }, { 'width': '150', 'height': '210' });
+    testPosition({ 'column': '1 / 3', 'row': '2 / 3' }, { 'width': '150', 'height': '140' });
+
+    // 2. spans.
+    testPosition({ 'column': '1 / span 2', 'row': '1 / span 1' }, { 'width': '150', 'height': '70' });
+    testPosition({ 'column': '1 / span 1', 'row': '1 / span 1' }, { 'width': '50', 'height': '70' });
+
+    testPosition({ 'column': '2 / span 1', 'row': '1 / span 1' }, { 'width': '100', 'height': '70' });
+    testPosition({ 'column': '2 / span 1', 'row': '1 / span 2' }, { 'width': '100', 'height': '210' });
+
+    testPosition({ 'column': 'span 2 / 3', 'row': 'span 2 / 3' }, { 'width': '150', 'height': '210' });
+    testPosition({ 'column': 'span 1 / 3', 'row': 'span 2 / 3' }, { 'width': '100', 'height': '210' });
+
+    testPosition({ 'column': 'span 2 / 3', 'row': 'span 1 / 3' }, { 'width': '150', 'height': '140' });
+    testPosition({ 'column': 'span 2 / 3', 'row': 'span 2 / 3' }, { 'width': '150', 'height': '210' });
+
+    // 3. Named grid lines, changing the explicit position.
+    testPosition({ 'column': '1 / 2 "col"', 'row': '1 / 2 "row"' }, { 'width': '50', 'height': '70' });
+    testPosition({ 'column': '1 / 3 "col"', 'row': '1 / 2 "row"' }, { 'width': '150', 'height': '70' });
+
+    testPosition({ 'column': '1 / 2 "col"', 'row': '1 / 3 "row"' }, { 'width': '50', 'height': '210' });
+    testPosition({ 'column': '1 / 2 "col"', 'row': '1 / 2 "row"' }, { 'width': '50', 'height': '70' });
+
+    testPosition({ 'column': '1 "col" / 4', 'row': '1 "row" / 4' }, { 'width': '350', 'height': '490' });
+    testPosition({ 'column': '2 "col" / 4', 'row': '1 "row" / 4' }, { 'width': '300', 'height': '490' });
+
+    testPosition({ 'column': '2 "col" / 4', 'row': '2 "row" / 4' }, { 'width': '300', 'height': '420' });
+    testPosition({ 'column': '2 "col" / 4', 'row': '1 "row" / 4' }, { 'width': '300', 'height': '490' });
+
+    // 4. Named grid lines, changing the name of the grid lines.
+    testPosition({ 'column': '1 / 3 "col"', 'row': '1 / 2 "row"' }, { 'width': '150', 'height': '70' });
+    testPosition({ 'column': '1 / 3 "invalid"', 'row': '1 / 2 "row"' }, { 'width': '50', 'height': '70' });
+
+    testPosition({ 'column': '1 / 4 "col"', 'row': '1 / 4 "invalid"' }, { 'width': '350', 'height': '70' });
+    testPosition({ 'column': '1 / 4 "col"', 'row': '1 / 4 "row"' }, { 'width': '350', 'height': '490' });
+
+    testPosition({ 'column': '2 "invalid" / 4', 'row': '1 "row" / 4' }, { 'width': '350', 'height': '490' });
+    testPosition({ 'column': '2 "col" / 4', 'row': '1 "row" / 4' }, { 'width': '300', 'height': '490' });
+
+    testPosition({ 'column': '2 "col" / 4', 'row': '2 "row" / 4' }, { 'width': '300', 'height': '420' });
+    testPosition({ 'column': '2 "col" / 4', 'row': '2 "invalid" / 4' }, { 'width': '300', 'height': '490' });
+
+    // 5. Span named grid lines, changing the grid line number.
+    testPosition({ 'column': '1 / span 3 "col"', 'row': '1 / span 2 "row"' }, { 'width': '350', 'height': '210' });
+    testPosition({ 'column': '1 / span 2 "col"', 'row': '1 / span 2 "row"' }, { 'width': '150', 'height': '210' });
+
+    testPosition({ 'column': '2 / span 3 "col"', 'row': '1 / span 2 "row"' }, { 'width': '300', 'height': '210' });
+    testPosition({ 'column': '2 / span 3 "col"', 'row': '1 / span 1 "row"' }, { 'width': '300', 'height': '70' });
+
+    testPosition({ 'column': 'span 2 "col" / 3', 'row': 'span 2 "row" / 4' }, { 'width': '150', 'height': '420' });
+    testPosition({ 'column': 'span 1 "col" / 3', 'row': 'span 2 "row" / 4' }, { 'width': '100', 'height': '420' });
+
+    testPosition({ 'column': 'span 2 "col" / 3', 'row': 'span 2 "row" / 4' }, { 'width': '150', 'height': '420' });
+    testPosition({ 'column': 'span 2 "col" / 3', 'row': 'span 3 "row" / 4' }, { 'width': '150', 'height': '490' });
+
+    // Test transition across grid lines types.
+    // 1. Explicit <-> spans.
+    testPosition({ 'column': '1 / 3', 'row': '1 / 2' }, { 'width': '150', 'height': '70' });
+    testPosition({ 'column': '1 / span 3', 'row': '1 / 2' }, { 'width': '350', 'height': '70' });
+
+    testPosition({ 'column': '1 / 3', 'row': '1 / span 2' }, { 'width': '150', 'height': '210' });
+    testPosition({ 'column': '1 / 3', 'row': '1 / 2' }, { 'width': '150', 'height': '70' });
+
+    testPosition({ 'column': 'span 1 / 3', 'row': '1 / 2' }, { 'width': '100', 'height': '70' });
+    testPosition({ 'column': '1 / 3', 'row': '1 / 2' }, { 'width': '150', 'height': '70' });
+
+    testPosition({ 'column': '1 / 3', 'row': '1 / 4' }, { 'width': '150', 'height': '490' });
+    testPosition({ 'column': '1 / 3', 'row': 'span 2 / 4' }, { 'width': '150', 'height': '420' });
+
+    // 2. Span <-> named grid lines.
+    testPosition({ 'column': '1 / "col" 3', 'row': '1 / span 4' }, { 'width': '150', 'height': '490' });
+    testPosition({ 'column': '1 / span 3', 'row': '1 / span 4' }, { 'width': '350', 'height': '490' });
+
+    testPosition({ 'column': '1 / "col" 3', 'row': '1 / span 3' }, { 'width': '150', 'height': '490' });
+    testPosition({ 'column': '1 / "col" 3', 'row': '1 / "row" 3' }, { 'width': '150', 'height': '210' });
+
+    testPosition({ 'column': 'span 1 / 3', 'row': 'span 2 / 4' }, { 'width': '100', 'height': '420' });
+    testPosition({ 'column': '1 "col" / 3', 'row': 'span 2 / 4' }, { 'width': '150', 'height': '420' });
+
+    testPosition({ 'column': 'span 1 / 3', 'row': '"col" 1 / 4' }, { 'width': '100', 'height': '490' });
+    testPosition({ 'column': 'span 1 / 3', 'row': 'span 1 / 4' }, { 'width': '100', 'height': '280' });
+
+    // 3. Named grid lines to span named grid line.
+    testPosition({ 'column': '1 / "col" 3', 'row': '1 / span 4' }, { 'width': '150', 'height': '490' });
+    testPosition({ 'column': '1 / span "col" 3', 'row': '1 / span 4' }, { 'width': '350', 'height': '490' });
+
+    testPosition({ 'column': '1 / "col" 3', 'row': '1 / span "row" 3' }, { 'width': '150', 'height': '490' });
+    testPosition({ 'column': '1 / "col" 3', 'row': '1 / "row" 3' }, { 'width': '150', 'height': '210' });
+
+    testPosition({ 'column': 'span "col" 1 / 3', 'row': 'span 2 / 4' }, { 'width': '100', 'height': '420' });
+    testPosition({ 'column': '1 "col" / 3', 'row': 'span 2 / 4' }, { 'width': '150', 'height': '420' });
+
+    testPosition({ 'column': 'span "col" 1 / 3', 'row': '"col" 1 / 4' }, { 'width': '100', 'height': '490' });
+    testPosition({ 'column': 'span "col" 1 / 3', 'row': 'span "col" 1 / 4' }, { 'width': '100', 'height': '280' });
+
+    // 4. Explicit <-> named grid lines.
+    // We need to modify the grid's definitions so that we have explicit and named grid lines not match anymore.
+    var gridElement = document.getElementsByClassName("grid")[0];
+    gridElement.classList.add("differentNamedGridLines");
+
+    testPosition({ 'column': '1 / "col4" 3', 'row': '1 / 4' }, { 'width': '350', 'height': '490' });
+    testPosition({ 'column': '1 / 3', 'row': '1 / 4' }, { 'width': '150', 'height': '490' });
+
+    testPosition({ 'column': '1 / "col4" 3', 'row': '1 / 4' }, { 'width': '350', 'height': '490' });
+    testPosition({ 'column': '1 / "col4" 3', 'row': '1 / "row3" 4' }, { 'width': '350', 'height': '210' });
+
+    testPosition({ 'column': '"col2" 1 / 4', 'row': '1 "row2" / 4' }, { 'width': '300', 'height': '420' });
+    testPosition({ 'column': '1 / 4', 'row': '1 "row2" / 4' }, { 'width': '350', 'height': '420' });
+
+    testPosition({ 'column': '"col2" 1 / 4', 'row': '1 / 4' }, { 'width': '300', 'height': '490' });
+    testPosition({ 'column': '"col2" 1 / 4', 'row': '1 "row2" / 4' }, { 'width': '300', 'height': '420' });
+
+    // 5. Span <-> span named grid lines.
+    testPosition({ 'column': '1 / span "col4" 2', 'row': '3 / span 1' }, { 'width': '350', 'height': '280' });
+    testPosition({ 'column': '1 / span 2', 'row': '3 / span 1' }, { 'width': '150', 'height': '280' });
+
+    testPosition({ 'column': '1 / span "col4" 3', 'row': '1 / span 4' }, { 'width': '350', 'height': '490' });
+    testPosition({ 'column': '1 / span "col4" 3', 'row': '1 / span "row3" 4' }, { 'width': '350', 'height': '210' });
+
+    testPosition({ 'column': 'span 2 / 4', 'row': 'span 1 / 4' }, { 'width': '300', 'height': '280' });
+    testPosition({ 'column': 'span "col1" 2 / 4', 'row': 'span 1 / 4' }, { 'width': '350', 'height': '280' });
+
+    testPosition({ 'column': 'span 2 / 4', 'row': 'span 1 / 4' }, { 'width': '300', 'height': '280' });
+    testPosition({ 'column': 'span 2 / 4', 'row': 'span "row2" 1 / 4' }, { 'width': '300', 'height': '420' });
+
+    // 6. Explicit to span named grid line.
+    testPosition({ 'column': '1 / 2', 'row': '2 / span "row3" 1' }, { 'width': '50', 'height': '140' });
+    testPosition({ 'column': '1 / span "col3" 2', 'row': '2 / span "row3" 1' }, { 'width': '150', 'height': '140' });
+
+    testPosition({ 'column': '1 / 2', 'row': '2 / span "row3" 4' }, { 'width': '50', 'height': '140' });
+    testPosition({ 'column': '1 / 2', 'row': '2 / 4' }, { 'width': '50', 'height': '420' });
+
+    testPosition({ 'column': 'span "col2" 1 / 4', 'row': 'span "row1" 3 / 4' }, { 'width': '300', 'height': '490' });
+    testPosition({ 'column': '1 / 4', 'row': 'span "row1" 3 / 4' }, { 'width': '350', 'height': '490' });
+
+    testPosition({ 'column': 'span "col2" 1 / 4', 'row': 'span "row1" 3 / 4' }, { 'width': '300', 'height': '490' });
+    testPosition({ 'column': 'span "col2" 1 / 4', 'row': '3 / 4' }, { 'width': '300', 'height': '280' });
+}
+window.addEventListener("load", updateGridItemPosition, false);
+</script>
+<body>
+
+<p>This test checks that we properly recompute our internal grid when a grid item is moved.</p>
+
+<div class="grid"><div class="sizedToGridArea"></div></div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/css-grid-layout/named-grid-lines-with-named-grid-areas-resolution-expected.txt b/LayoutTests/fast/css-grid-layout/named-grid-lines-with-named-grid-areas-resolution-expected.txt
new file mode 100644 (file)
index 0000000..63a0833
--- /dev/null
@@ -0,0 +1,18 @@
+This test checks that we resolve named grid line per the specification.
+
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
diff --git a/LayoutTests/fast/css-grid-layout/named-grid-lines-with-named-grid-areas-resolution.html b/LayoutTests/fast/css-grid-layout/named-grid-lines-with-named-grid-areas-resolution.html
new file mode 100644 (file)
index 0000000..077e1b4
--- /dev/null
@@ -0,0 +1,199 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner)
+    testRunner.overridePreference("WebKitCSSGridLayoutEnabled", 1);
+</script>
+<link href="resources/grid.css" rel="stylesheet">
+<style type="text/css">
+
+  .gridAreas {
+      -webkit-grid-template-areas: ". a a"
+                                   "c a a"
+                                   ". . d";
+  }
+
+  .gridNoLineNames {
+      -webkit-grid-template-columns: 50px 100px 200px;
+      -webkit-grid-template-rows: 50px 100px 200px;
+  }
+
+  .gridUniqueNames {
+      -webkit-grid-template-columns: "a" 50px "b" "b-start" 100px "c" 200px "d";
+      -webkit-grid-template-rows: "e" 50px "f" 100px "g" "g-start" 200px "h";
+  }
+
+  .gridWithNamedLineBeforeGridArea {
+      -webkit-grid-template-columns: "a-start" "c-start" 50px "d-start" 100px 200px;
+      -webkit-grid-template-rows: "c-start" 50px "d-start" 100px 200px;
+  }
+
+  .gridWithNamedLineAfterGridArea {
+      -webkit-grid-template-columns: 50px 100px "a-start" 200px;
+      -webkit-grid-template-rows: 50px 100px "c-start" 200px;
+  }
+
+  .gridWithEndLines {
+      -webkit-grid-template-columns: 50px 100px "a-end" 200px "c-end";
+      -webkit-grid-template-rows: 50px "c-end" 100px "d-end" 200px;
+  }
+
+  .gridRepeatedNames {
+      -webkit-grid-template-columns: "d-start" 50px "d-start" 100px "d-start" 200px;
+      -webkit-grid-template-rows: 50px "c-end" 100px "c-end" 200px "c-end";
+  }
+
+</style>
+<script src="../../resources/check-layout.js"></script>
+</head>
+<body onload="checkLayout('.grid')">
+
+<p>This test checks that we resolve named grid line per the specification.</p>
+
+<!-- Check positioning using unique grid-line names -->
+<div style="position: relative">
+  <div class="grid gridUniqueNames">
+    <div class="sizedToGridArea" style="-webkit-grid-column: b;" data-offset-x="50" data-offset-y="0" data-expected-width="100" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: e;" data-offset-x="0" data-offset-y="0" data-expected-width="50" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: b-start;" data-offset-x="50" data-offset-y="0" data-expected-width="100" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: g-start;" data-offset-x="0" data-offset-y="150" data-expected-width="50" data-expected-height="200"></div>
+  </div>
+</div>
+
+<div style="position: relative">
+  <div class="grid gridUniqueNames">
+    <div class="sizedToGridArea" style="-webkit-grid-column: b / d;" data-offset-x="50" data-offset-y="0" data-expected-width="300" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: g / h;" data-offset-x="0" data-offset-y="150" data-expected-width="50" data-expected-height="200"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: c; -webkit-grid-row: f;" data-offset-x="150" data-offset-y="50" data-expected-width="200" data-expected-height="100"></div>
+  </div>
+</div>
+
+<!-- Check positioning using unique grid-line names mixed with integers -->
+<div style="position: relative">
+  <div class="grid gridUniqueNames">
+    <div class="sizedToGridArea" style="-webkit-grid-column: b / 4;" data-offset-x="50" data-offset-y="0" data-expected-width="300" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: 3 / h;" data-offset-x="0" data-offset-y="150" data-expected-width="50" data-expected-height="200"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: 2; -webkit-grid-row: g;" data-offset-x="50" data-offset-y="150" data-expected-width="100" data-expected-height="200"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: a; -webkit-grid-row: 2;" data-offset-x="0" data-offset-y="50" data-expected-width="50" data-expected-height="100"></div>
+  </div>
+</div>
+
+<!-- Check that without named gridAreas there are no implicit grid-line names defined -->
+<div style="position: relative">
+  <div class="grid gridUniqueNames">
+    <div class="sizedToGridArea" style="-webkit-grid-column: c-start;" data-offset-x="0" data-offset-y="0" data-expected-width="50" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: f-start;" data-offset-x="0" data-offset-y="0" data-expected-width="50" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: c-start; -webkit-grid-row: f-end" data-offset-x="0" data-offset-y="0" data-expected-width="50" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: b-end; -webkit-grid-row: h-start" data-offset-x="0" data-offset-y="0" data-expected-width="50" data-expected-height="50"></div>
+  </div>
+</div>
+
+<!-- Check that gridArea's implicit names are well defined -->
+<div style="position: relative">
+  <div class="grid gridAreas gridNoLineNames">
+    <div class="sizedToGridArea" style="-webkit-grid-column: a-start; -webkit-grid-row: d-start;" data-offset-x="50" data-offset-y="150" data-expected-width="100" data-expected-height="200"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: a-start; -webkit-grid-row: d;" data-offset-x="50" data-offset-y="150" data-expected-width="100" data-expected-height="200"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: d-start; -webkit-grid-row: c-start;" data-offset-x="150" data-offset-y="50" data-expected-width="200" data-expected-height="100"></div>
+  </div>
+</div>
+
+<!-- Check positioning using grid areas -->
+<div style="position: relative">
+  <div class="grid gridAreas gridNoLineNames">
+    <div class="sizedToGridArea" style="-webkit-grid-column: d;" data-offset-x="150" data-offset-y="0" data-expected-width="200" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: d;" data-offset-x="0" data-offset-y="150" data-expected-width="50" data-expected-height="200"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: c;" data-offset-x="0" data-offset-y="0" data-expected-width="50" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: c;" data-offset-x="0" data-offset-y="50" data-expected-width="50" data-expected-height="100"></div>
+  </div>
+</div>
+
+<div style="position: relative">
+  <div class="grid gridAreas gridNoLineNames">
+    <div class="sizedToGridArea" style="-webkit-grid-column: a;" data-offset-x="50" data-offset-y="0" data-expected-width="300" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: a;" data-offset-x="0" data-offset-y="0" data-expected-width="50" data-expected-height="150"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: a; -webkit-grid-row: a;" data-offset-x="50" data-offset-y="0" data-expected-width="300" data-expected-height="150"></div>
+  </div>
+</div>
+
+<!-- Use grid area's implicit line names if defined before explicitly named grid lines -->
+<div style="position: relative">
+  <div class="grid gridAreas gridWithNamedLineAfterGridArea">
+    <div class="sizedToGridArea" style="-webkit-grid-column: d;" data-offset-x="150" data-offset-y="0" data-expected-width="200" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: d;" data-offset-x="0" data-offset-y="150" data-expected-width="50" data-expected-height="200"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: c;" data-offset-x="0" data-offset-y="0" data-expected-width="50" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: c;" data-offset-x="0" data-offset-y="50" data-expected-width="50" data-expected-height="100"></div>
+  </div>
+</div>
+
+<div style="position: relative">
+  <div class="grid gridAreas gridWithNamedLineAfterGridArea">
+    <div class="sizedToGridArea" style="-webkit-grid-column-start: a-start;" data-offset-x="50" data-offset-y="0" data-expected-width="100" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row-start: c-start;" data-offset-x="0" data-offset-y="50" data-expected-width="50" data-expected-height="100"></div>
+  </div>
+</div>
+
+<div style="position: relative">
+  <div class="grid gridAreas gridWithNamedLineAfterGridArea">
+    <div class="sizedToGridArea" style="-webkit-grid-column: a;" data-offset-x="50" data-offset-y="0" data-expected-width="300" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: d;" data-offset-x="0" data-offset-y="150" data-expected-width="50" data-expected-height="200"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: a; -webkit-grid-row: d;" data-offset-x="50" data-offset-y="150" data-expected-width="300" data-expected-height="200"></div>
+  </div>
+</div>
+
+<!-- Use explicitly named grid lines if they're defined before the grid area -->
+<div style="position: relative">
+  <div class="grid gridAreas gridWithNamedLineBeforeGridArea">
+    <div class="sizedToGridArea" style="-webkit-grid-column: d;" data-offset-x="50" data-offset-y="0" data-expected-width="300" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: d;" data-offset-x="0" data-offset-y="50" data-expected-width="50" data-expected-height="300"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: c;" data-offset-x="0" data-offset-y="0" data-expected-width="50" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: c;" data-offset-x="0" data-offset-y="0" data-expected-width="50" data-expected-height="150"></div>
+  </div>
+</div>
+
+<div style="position: relative">
+  <div class="grid gridAreas gridWithNamedLineBeforeGridArea">
+    <div class="sizedToGridArea" style="-webkit-grid-column-start: d-start;" data-offset-x="50" data-offset-y="0" data-expected-width="100" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row-start: d-start;" data-offset-x="0" data-offset-y="50" data-expected-width="50" data-expected-height="100"></div>
+  </div>
+</div>
+
+<div style="position: relative">
+  <div class="grid gridAreas gridWithNamedLineBeforeGridArea">
+    <div class="sizedToGridArea" style="-webkit-grid-column: a;" data-offset-x="0" data-offset-y="0" data-expected-width="350" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: d;" data-offset-x="0" data-offset-y="50" data-expected-width="50" data-expected-height="300"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: a; -webkit-grid-row: d;" data-offset-x="0" data-offset-y="50" data-expected-width="350" data-expected-height="300"></div>
+  </div>
+</div>
+
+<!-- Check that a "-start" ident in a end column or a "-end" ident in a start column is not treated as a implicit grid line of a grid area -->
+<div style="position: relative">
+  <div class="grid gridAreas gridNoLineNames">
+    <div class="sizedToGridArea" style="-webkit-grid-column: a / a-start; -webkit-grid-row: d-start;" data-offset-x="50" data-offset-y="150" data-expected-width="100" data-expected-height="200"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: d / d-start; -webkit-grid-row: c-start;" data-offset-x="150" data-offset-y="50" data-expected-width="200" data-expected-height="100"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: c; -webkit-grid-row: a / a-start;" data-offset-x="0" data-offset-y="0" data-expected-width="50" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: d; -webkit-grid-row: c / c-start;" data-offset-x="150" data-offset-y="50" data-expected-width="200" data-expected-height="100"></div>
+  </div>
+</div>
+
+<!-- Check that we propertly resolve explicit "-end" lines inside grid areas -->
+<div style="position: relative">
+  <div class="grid gridAreas gridWithEndLines">
+    <div class="sizedToGridArea" style="-webkit-grid-column: a" data-offset-x="50" data-offset-y="0" data-expected-width="100" data-expected-height="50"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: a; -webkit-grid-row: c" data-offset-x="50" data-offset-y="50" data-expected-width="100" data-expected-height="100"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-row: d;" data-offset-x="0" data-offset-y="150" data-expected-width="50" data-expected-height="200"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: a; -webkit-grid-row: d;" data-offset-x="50" data-offset-y="150" data-expected-width="100" data-expected-height="200"></div>
+  </div>
+</div>
+
+<!-- Check that we always pick the first definition when multiple grid lines have the same name -->
+<div style="position: relative">
+  <div class="grid gridAreas gridRepeatedNames">
+    <div class="sizedToGridArea" style="-webkit-grid-column: d; -webkit-grid-row: c" data-offset-x="0" data-offset-y="50" data-expected-width="350" data-expected-height="100"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: d-start / d-end; -webkit-grid-row: c-start / c-end" data-offset-x="0" data-offset-y="50" data-expected-width="350" data-expected-height="100"></div>
+    <div class="sizedToGridArea" style="-webkit-grid-column: c; -webkit-grid-row: d" data-offset-x="0" data-offset-y="150" data-expected-width="50" data-expected-height="200"></div>
+  </div>
+</div>
+
+</body>
+</html>
index cc52d4e..b418bff 100644 (file)
@@ -1,3 +1,55 @@
+2014-02-27  Sergio Villar Senin  <svillar@igalia.com>
+
+        [CSS Grid Layout] Fix positioning grid items using named grid lines/areas
+        https://bugs.webkit.org/show_bug.cgi?id=129372
+
+        Reviewed by Darin Adler.
+
+        Our code was assuming that a <custom-ident> in
+        -webkit-grid-{column|row}-{start|end} and
+        -webkit-grid-{column|row} was always a grid area name. That's
+        wrong because the <custom-ident> could be also a explicitly named
+        grid line or the an implicitly named grid line created by a grid
+        area definition.
+
+        The style resolution code was not correct either. This patch fixes
+        it so it now matches the spec, which means that:
+        - first we try to match any existing grid area.
+        - then if there is a named grid line with the name
+        <custom-ident>-{start|end} for -webkit-grid-{column|row}-{start|end}
+        defined before the grid area then we use it instead of the grid
+        area.
+        - otherwise if there is a named grid line we resolve to the first such line.
+        - otherwise we treat it as 'auto'.
+
+        Fixing this uncovered a bug in GridPosition, we were not using the
+        name of the stored grid area to check if two GridPositions were
+        the same.
+
+        Tests: fast/css-grid-layout/grid-item-position-changed-dynamic.html
+               fast/css-grid-layout/named-grid-lines-with-named-grid-areas-resolution.html
+
+        * css/StyleResolver.cpp:
+        (WebCore::gridLineDefinedBeforeGridArea): New function to check if
+        a given named grid line was defined before an implicit named grid
+        line created by a grid area definition.
+        (WebCore::StyleResolver::adjustNamedGridItemPosition): New
+        function that adjusts the position of a GridPosition parsed as a
+        grid area.
+        (WebCore::StyleResolver::adjustGridItemPosition): Use the new
+        function adjustNamedGridItemPosition to adjust the positions of
+        named grid lines.
+        * css/StyleResolver.h:
+        * rendering/RenderGrid.cpp:
+        (WebCore::RenderGrid::resolveNamedGridLinePositionFromStyle): Use GridPosition:: namespace.
+        (WebCore::RenderGrid::resolveGridPositionFromStyle): Ditto.
+        (WebCore::RenderGrid::resolveRowEndColumnEndNamedGridLinePositionAgainstOppositePosition): Ditto.
+        * rendering/RenderGrid.h:
+        * rendering/style/GridPosition.h:
+        (WebCore::GridPosition::adjustGridPositionForRowEndColumnEndSide): Moved from RenderGrid.cpp.
+        (WebCore::GridPosition::adjustGridPositionForSide): Ditto.
+        (WebCore::GridPosition::operator==): Use the named grid line to check equality.
+
 2014-02-28  Zoltan Horvath  <zoltan@webkit.org>
 
         [CSS Shapes] Update line segments for ShapeInside only if the new line is wide enough
index a7d300f..dc47425 100644 (file)
@@ -9,6 +9,7 @@
  * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
  * Copyright (C) 2012 Google Inc. All rights reserved.
+ * Copyright (C) 2014 Igalia S.L.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -1376,6 +1377,82 @@ void StyleResolver::adjustRenderStyle(RenderStyle& style, const RenderStyle& par
 }
 
 #if ENABLE(CSS_GRID_LAYOUT)
+static inline bool gridLineDefinedBeforeGridArea(const String& gridLineName, const String& gridAreaName, const NamedGridAreaMap& gridAreaMap, const NamedGridLinesMap& namedLinesMap, GridPositionSide side)
+{
+    ASSERT(namedLinesMap.contains(gridLineName));
+    // Grid line indexes are inserted in order.
+    size_t namedGridLineFirstDefinition = GridPosition::adjustGridPositionForSide(namedLinesMap.get(gridLineName)[0], side);
+
+    ASSERT(gridAreaMap.contains(gridAreaName));
+    const GridCoordinate& gridAreaCoordinates = gridAreaMap.get(gridAreaName);
+
+    // GridCoordinate refers to tracks while the indexes in namedLinesMap refer to lines, that's why we need to add 1 to
+    // the grid coordinate to get the end line index.
+    switch (side) {
+    case ColumnStartSide:
+        return namedGridLineFirstDefinition < gridAreaCoordinates.columns.initialPositionIndex;
+    case ColumnEndSide:
+        return namedGridLineFirstDefinition < gridAreaCoordinates.columns.finalPositionIndex;
+    case RowStartSide:
+        return namedGridLineFirstDefinition < gridAreaCoordinates.rows.initialPositionIndex;
+    case RowEndSide:
+        return namedGridLineFirstDefinition < gridAreaCoordinates.rows.finalPositionIndex;
+    }
+    ASSERT_NOT_REACHED();
+    return false;
+}
+
+std::unique_ptr<GridPosition> StyleResolver::adjustNamedGridItemPosition(const NamedGridAreaMap& gridAreaMap, const NamedGridLinesMap& namedLinesMap, const GridPosition& position, GridPositionSide side) const
+{
+    ASSERT(position.isNamedGridArea());
+    // The StyleBuilder always treats <custom-ident> as a named grid area. We must decide here if they are going to be
+    // resolved to either a grid area or a grid line.
+
+    String namedGridAreaOrGridLine = position.namedGridLine();
+    bool hasStartSuffix = namedGridAreaOrGridLine.endsWith("-start");
+    bool hasEndSuffix = namedGridAreaOrGridLine.endsWith("-end");
+    bool isStartSide = side == ColumnStartSide || side == RowStartSide;
+    bool hasStartSuffixForStartSide = hasStartSuffix && isStartSide;
+    bool hasEndSuffixForEndSide = hasEndSuffix && !isStartSide;
+    size_t suffixLength = hasStartSuffix ? strlen("-start") : strlen("-end");
+    String gridAreaName = hasStartSuffixForStartSide || hasEndSuffixForEndSide ? namedGridAreaOrGridLine.substring(0, namedGridAreaOrGridLine.length() - suffixLength) : namedGridAreaOrGridLine;
+
+    if (gridAreaMap.contains(gridAreaName)) {
+        String gridLineName;
+        if (isStartSide && !hasStartSuffix)
+            gridLineName = namedGridAreaOrGridLine + "-start";
+        else if (!isStartSide && !hasEndSuffix)
+            gridLineName = namedGridAreaOrGridLine + "-end";
+        else
+            gridLineName = namedGridAreaOrGridLine;
+
+        if (namedLinesMap.contains(gridLineName) && gridLineDefinedBeforeGridArea(gridLineName, gridAreaName, gridAreaMap, namedLinesMap, side)) {
+            // Use the explicitly defined grid line defined before the grid area instead of the grid area.
+            auto adjustedPosition = std::make_unique<GridPosition>();
+            adjustedPosition->setExplicitPosition(1, gridLineName);
+            return adjustedPosition;
+        }
+
+        if (hasStartSuffixForStartSide || hasEndSuffixForEndSide) {
+            // Renderer expects the grid area name instead of the implicit grid line name.
+            auto adjustedPosition = std::make_unique<GridPosition>();
+            adjustedPosition->setNamedGridArea(gridAreaName);
+            return adjustedPosition;
+        }
+
+        return nullptr;
+    }
+
+    if (namedLinesMap.contains(namedGridAreaOrGridLine)) {
+        auto adjustedPosition = std::make_unique<GridPosition>();
+        adjustedPosition->setExplicitPosition(1, namedGridAreaOrGridLine);
+        return adjustedPosition;
+    }
+
+    // We must clear unknown named grid areas
+    return std::make_unique<GridPosition>();
+}
+
 void StyleResolver::adjustGridItemPosition(RenderStyle& style, const RenderStyle& parentStyle) const
 {
     const GridPosition& columnStartPosition = style.gridItemColumnStart();
@@ -1394,17 +1471,27 @@ void StyleResolver::adjustGridItemPosition(RenderStyle& style, const RenderStyle
         style.setGridItemRowEnd(GridPosition());
     }
 
-    // Unknown named grid area compute to 'auto'.
-    const NamedGridAreaMap& map = parentStyle.namedGridArea();
+    // If the grid position is a single <custom-ident> then the spec mandates us to resolve it following this steps:
+    //     * If there is a named grid area called <custom-ident> resolve the position to the area's corresponding edge.
+    //         * If a grid area was found with that name, check that there is no <custom-ident>-start or <custom-ident>-end (depending
+    // on the css property being defined) specified before the grid area. If that's the case resolve to that grid line.
+    //     * Otherwise check if there is a grid line named <custom-ident>.
+    //     * Otherwise treat it as auto.
+    const NamedGridLinesMap& namedGridColumnLines = parentStyle.namedGridColumnLines();
+    const NamedGridLinesMap& namedGridRowLines = parentStyle.namedGridRowLines();
+    const NamedGridAreaMap& gridAreaMap = parentStyle.namedGridArea();
 
-#define CLEAR_UNKNOWN_NAMED_AREA(prop, Prop) \
-    if (prop.isNamedGridArea() && !map.contains(prop.namedGridLine())) \
-        style.setGridItem##Prop(GridPosition());
+#define ADJUST_GRID_POSITION_MAYBE(position, Prop, namedGridLines, side) \
+    if (position.isNamedGridArea()) {                                   \
+        std::unique_ptr<GridPosition> adjustedPosition = adjustNamedGridItemPosition(gridAreaMap, namedGridLines, position, side); \
+        if (adjustedPosition)                                           \
+            style.setGridItem##Prop(*adjustedPosition);                 \
+    }
 
-    CLEAR_UNKNOWN_NAMED_AREA(columnStartPosition, ColumnStart);
-    CLEAR_UNKNOWN_NAMED_AREA(columnEndPosition, ColumnEnd);
-    CLEAR_UNKNOWN_NAMED_AREA(rowStartPosition, RowStart);
-    CLEAR_UNKNOWN_NAMED_AREA(rowEndPosition, RowEnd);
+    ADJUST_GRID_POSITION_MAYBE(columnStartPosition, ColumnStart, namedGridColumnLines, ColumnStartSide);
+    ADJUST_GRID_POSITION_MAYBE(columnEndPosition, ColumnEnd, namedGridColumnLines, ColumnEndSide);
+    ADJUST_GRID_POSITION_MAYBE(rowStartPosition, RowStart, namedGridRowLines, RowStartSide);
+    ADJUST_GRID_POSITION_MAYBE(rowEndPosition, RowEnd, namedGridRowLines, RowEndSide);
 }
 #endif /* ENABLE(CSS_GRID_LAYOUT) */
 
index ab7de43..9854350 100644 (file)
@@ -296,6 +296,7 @@ private:
     void adjustRenderStyle(RenderStyle& styleToAdjust, const RenderStyle& parentStyle, Element*);
 #if ENABLE(CSS_GRID_LAYOUT)
     void adjustGridItemPosition(RenderStyle& styleToAdjust, const RenderStyle& parentStyle) const;
+    std::unique_ptr<GridPosition> adjustNamedGridItemPosition(const NamedGridAreaMap&, const NamedGridLinesMap&, const GridPosition&, GridPositionSide) const;
 #endif
 
     bool fastRejectSelector(const RuleData&) const;
index fcafe04..8277179 100644 (file)
@@ -893,20 +893,6 @@ PassOwnPtr<GridSpan> RenderGrid::resolveGridPositionsFromStyle(const RenderBox*
     return adoptPtr(new GridSpan(resolvedInitialPosition, resolvedFinalPosition));
 }
 
-inline static size_t adjustGridPositionForRowEndColumnEndSide(size_t resolvedPosition)
-{
-    return resolvedPosition ? resolvedPosition - 1 : 0;
-}
-
-static size_t adjustGridPositionForSide(size_t resolvedPosition, RenderGrid::GridPositionSide side)
-{
-    // An item finishing on the N-th line belongs to the N-1-th cell.
-    if (side == RenderGrid::ColumnEndSide || side == RenderGrid::RowEndSide)
-        return adjustGridPositionForRowEndColumnEndSide(resolvedPosition);
-
-    return resolvedPosition;
-}
-
 size_t RenderGrid::resolveNamedGridLinePositionFromStyle(const GridPosition& position, GridPositionSide side) const
 {
     ASSERT(!position.namedGridLine().isNull());
@@ -917,7 +903,7 @@ size_t RenderGrid::resolveNamedGridLinePositionFromStyle(const GridPosition& pos
         if (position.isPositive())
             return 0;
         const size_t lastLine = explicitGridSizeForSide(side);
-        return adjustGridPositionForSide(lastLine, side);
+        return GridPosition::adjustGridPositionForSide(lastLine, side);
     }
 
     size_t namedGridLineIndex;
@@ -925,7 +911,7 @@ size_t RenderGrid::resolveNamedGridLinePositionFromStyle(const GridPosition& pos
         namedGridLineIndex = std::min<size_t>(position.integerPosition(), it->value.size()) - 1;
     else
         namedGridLineIndex = std::max<int>(it->value.size() - abs(position.integerPosition()), 0);
-    return adjustGridPositionForSide(it->value[namedGridLineIndex], side);
+    return GridPosition::adjustGridPositionForSide(it->value[namedGridLineIndex], side);
 }
 
 size_t RenderGrid::resolveGridPositionFromStyle(const GridPosition& position, GridPositionSide side) const
@@ -939,7 +925,7 @@ size_t RenderGrid::resolveGridPositionFromStyle(const GridPosition& position, Gr
 
         // Handle <integer> explicit position.
         if (position.isPositive())
-            return adjustGridPositionForSide(position.integerPosition() - 1, side);
+            return GridPosition::adjustGridPositionForSide(position.integerPosition() - 1, side);
 
         size_t resolvedPosition = abs(position.integerPosition()) - 1;
         const size_t endOfTrack = explicitGridSizeForSide(side);
@@ -948,7 +934,7 @@ size_t RenderGrid::resolveGridPositionFromStyle(const GridPosition& position, Gr
         if (endOfTrack < resolvedPosition)
             return 0;
 
-        return adjustGridPositionForSide(endOfTrack - resolvedPosition, side);
+        return GridPosition::adjustGridPositionForSide(endOfTrack - resolvedPosition, side);
     }
     case NamedGridAreaPosition:
     {
@@ -1048,7 +1034,7 @@ PassOwnPtr<GridSpan> RenderGrid::resolveRowEndColumnEndNamedGridLinePositionAgai
         firstLineAfterOppositePositionIndex = firstLineAfterOppositePosition - gridLines.begin();
 
     size_t gridLineIndex = std::min(gridLines.size() - 1, firstLineAfterOppositePositionIndex + position.spanPosition() - 1);
-    size_t resolvedGridLinePosition = adjustGridPositionForRowEndColumnEndSide(gridLines[gridLineIndex]);
+    size_t resolvedGridLinePosition = GridPosition::adjustGridPositionForRowEndColumnEndSide(gridLines[gridLineIndex]);
     if (resolvedGridLinePosition < resolvedOppositePosition)
         resolvedGridLinePosition = resolvedOppositePosition;
     return GridSpan::create(resolvedOppositePosition, resolvedGridLinePosition);
index 19d0f84..d751393 100644 (file)
@@ -55,13 +55,6 @@ public:
     virtual bool avoidsFloats() const override { return true; }
     virtual bool canCollapseAnonymousBlockChild() const override { return false; }
 
-    enum GridPositionSide {
-        ColumnStartSide,
-        ColumnEndSide,
-        RowStartSide,
-        RowEndSide
-    };
-
     const Vector<LayoutUnit>& columnPositions() const { return m_columnPositions; }
     const Vector<LayoutUnit>& rowPositions() const { return m_rowPositions; }
 
index f539b89..1624c0c 100644 (file)
@@ -45,6 +45,13 @@ enum GridPositionType {
     NamedGridAreaPosition // <ident>
 };
 
+enum GridPositionSide {
+    ColumnStartSide,
+    ColumnEndSide,
+    RowStartSide,
+    RowEndSide
+};
+
 class GridPosition {
 public:
     GridPosition()
@@ -53,6 +60,20 @@ public:
     {
     }
 
+    static inline size_t adjustGridPositionForRowEndColumnEndSide(size_t resolvedPosition)
+    {
+        return resolvedPosition ? resolvedPosition - 1 : 0;
+    }
+
+    static size_t adjustGridPositionForSide(size_t resolvedPosition, GridPositionSide side)
+    {
+        // An item finishing on the N-th line belongs to the N-1-th cell.
+        if (side == ColumnEndSide || side == RowEndSide)
+            return adjustGridPositionForRowEndColumnEndSide(resolvedPosition);
+
+        return resolvedPosition;
+    }
+
     bool isPositive() const { return integerPosition() > 0; }
 
     GridPositionType type() const { return m_type; }
@@ -103,7 +124,7 @@ public:
 
     bool operator==(const GridPosition& other) const
     {
-        return m_type == other.m_type && m_integerPosition == other.m_integerPosition;
+        return m_type == other.m_type && m_integerPosition == other.m_integerPosition && m_namedGridLine == other.m_namedGridLine;
     }
 
     bool shouldBeResolvedAgainstOppositePosition() const