[css-grid] Clamp the number of autorepeat tracks
authorsvillar@igalia.com <svillar@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 30 Mar 2017 14:40:46 +0000 (14:40 +0000)
committersvillar@igalia.com <svillar@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 30 Mar 2017 14:40:46 +0000 (14:40 +0000)
https://bugs.webkit.org/show_bug.cgi?id=170120

Reviewed by Manuel Rego Casasnovas.

Source/WebCore:

As suggested by the specs we do clamp the maximum number of tracks per grid in order to
minimize potential OOM situations. However we were not considering the case of the recently
added auto repeat syntax. Abnormally huge values for the width/height on the grid container
could lead to a number of auto repeat tracks higher than the maximum.

A new API was added to Internals in order to test limits without having to create huge
grids. This new API allows clients to set an arbitrary limit to the number of tracks. The
addition of this new API forced us to add GridPosition.cpp to the project to define the
global variable we use for testing. We took the chance to move part of the implementation
from the header file to the source file.

Last but not least, several new ASSERTs were added to Grid.cpp implementation to ensure that
we do not surpass the grid track limits.

Test: fast/css-grid-layout/grid-auto-repeat-huge-grid.html

* CMakeLists.txt:
* WebCore.xcodeproj/project.pbxproj:
* css/parser/CSSPropertyParser.cpp:
(WebCore::consumeGridTrackRepeatFunction):
* rendering/Grid.cpp:
(WebCore::Grid::ensureGridSize): Added ASSERT.
(WebCore::Grid::setSmallestTracksStart): Ditto.
(WebCore::Grid::setAutoRepeatTracks): Ditto.
(WebCore::Grid::setAutoRepeatEmptyColumns): Ditto.
(WebCore::Grid::setAutoRepeatEmptyRows): Ditto.
* rendering/RenderGrid.cpp:
(WebCore::RenderGrid::clampAutoRepeatTracks): New method.
(WebCore::RenderGrid::placeItemsOnGrid): Clamp the number of auto repeat tracks before
passing them to the Grid.
* rendering/RenderGrid.h:
* rendering/style/GridArea.h:
(WebCore::GridSpan::GridSpan):
* rendering/style/GridPosition.cpp: Added.
(WebCore::GridPosition::setExplicitPosition):
(WebCore::GridPosition::setAutoPosition):
(WebCore::GridPosition::setSpanPosition):
(WebCore::GridPosition::setNamedGridArea):
(WebCore::GridPosition::integerPosition):
(WebCore::GridPosition::namedGridLine):
(WebCore::GridPosition::spanPosition):
(WebCore::GridPosition::operator==):
* rendering/style/GridPosition.h:
(WebCore::GridPosition::shouldBeResolvedAgainstOppositePosition):
(WebCore::GridPosition::max):
(WebCore::GridPosition::min):
(WebCore::GridPosition::setMaxPositionForTesting):
(WebCore::GridPosition::setExplicitPosition): Deleted.
(WebCore::GridPosition::setAutoPosition): Deleted.
(WebCore::GridPosition::setSpanPosition): Deleted.
(WebCore::GridPosition::setNamedGridArea): Deleted.
(WebCore::GridPosition::integerPosition): Deleted.
(WebCore::GridPosition::namedGridLine): Deleted.
(WebCore::GridPosition::spanPosition): Deleted.
(WebCore::GridPosition::operator==): Deleted.
* rendering/style/GridPositionsResolver.cpp:
(WebCore::GridPositionsResolver::explicitGridColumnCount):
(WebCore::GridPositionsResolver::explicitGridRowCount):
* testing/Internals.cpp:
(WebCore::Internals::setGridMaxTracksLimit):
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

Added a new test to check the clamping of grid tracks. It's mostly all JavaScript in order
to allow us to run several test cases over a small number of grids in a single file quite
fast. The other option was to statically (CSS) create several huge grids, something that
could potentially be very expensive in terms of memory and CPU leading to timeouts.

* fast/css-grid-layout/grid-auto-repeat-huge-grid-expected.txt: Added.
* fast/css-grid-layout/grid-auto-repeat-huge-grid.html: Added.

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

17 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/css-grid-layout/grid-auto-repeat-huge-grid-expected.txt [new file with mode: 0644]
LayoutTests/fast/css-grid-layout/grid-auto-repeat-huge-grid.html [new file with mode: 0644]
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/css/parser/CSSPropertyParser.cpp
Source/WebCore/rendering/Grid.cpp
Source/WebCore/rendering/RenderGrid.cpp
Source/WebCore/rendering/RenderGrid.h
Source/WebCore/rendering/style/GridArea.h
Source/WebCore/rendering/style/GridPosition.cpp [new file with mode: 0644]
Source/WebCore/rendering/style/GridPosition.h
Source/WebCore/rendering/style/GridPositionsResolver.cpp
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl

index 2d54999..b6b192d 100644 (file)
@@ -1,3 +1,18 @@
+2017-03-27  Sergio Villar Senin  <svillar@igalia.com>
+
+        [css-grid] Clamp the number of autorepeat tracks
+        https://bugs.webkit.org/show_bug.cgi?id=170120
+
+        Reviewed by Manuel Rego Casasnovas.
+
+        Added a new test to check the clamping of grid tracks. It's mostly all JavaScript in order
+        to allow us to run several test cases over a small number of grids in a single file quite
+        fast. The other option was to statically (CSS) create several huge grids, something that
+        could potentially be very expensive in terms of memory and CPU leading to timeouts.
+
+        * fast/css-grid-layout/grid-auto-repeat-huge-grid-expected.txt: Added.
+        * fast/css-grid-layout/grid-auto-repeat-huge-grid.html: Added.
+
 2017-03-30  Antoine Quint  <graouts@apple.com>
 
         [mac-wk1] LayoutTest media/modern-media-controls/airplay-button/airplay-button.html is a flaky timeout
diff --git a/LayoutTests/fast/css-grid-layout/grid-auto-repeat-huge-grid-expected.txt b/LayoutTests/fast/css-grid-layout/grid-auto-repeat-huge-grid-expected.txt
new file mode 100644 (file)
index 0000000..90cd487
--- /dev/null
@@ -0,0 +1,22 @@
+This test checks that we properly enforce the maximum number of grid tracks in different situations.
+
+
+PASS Test that we don't get more than kGridMaxTracks repetitions even on very wide grids (normal tracks clamped). 
+PASS Test that we don't get more than kGridMaxTracks repetitions even on very tall grids (normal tracks clamped). 
+PASS Test that we don't get more than kGridMaxTracks repetitions even on very wide grids (auto repeat tracks clamped). 
+PASS Test that we don't get more than kGridMaxTracks repetitions even on very tall grids (auto repeat tracks clamped). 
+PASS Test that we don't get more than kGridMaxTracks repetitions even on very wide grids with gaps. 
+PASS Test that we don't get more than kGridMaxTracks repetitions even on very tall grids with gaps. 
+PASS Test that we don't get more than kGridMaxTracks repetitions even on very wide grids with gaps and min-width. 
+PASS Test that we don't get more than kGridMaxTracks repetitions even on very tall grids with gaps and min-height. 
+PASS Test that we don't crash when there are exactly kGridMaxTracks auto repeat rows on very tall grids. 
+PASS Test that we don't crash when there are exactly kGridMaxTracks auto repeat columns on very wide grids. 
+PASS Test that we don't crash when there are exactly kGridMaxTracks (normal and auto-repeat) rows on very tall grids. 
+PASS Test that we don't crash when there are exactly kGridMaxTracks (normal and auto-repeat) columns on very wide grids. 
+PASS Test that we don't crash when there are exactly kGridMaxTracks non auto-repeat rows on very tall grids. 
+PASS Test that we don't crash when there are exactly kGridMaxTracks non auto-repeat columns on very wide grids. 
+PASS Test that we don't crash when there are exactly kGridMaxTracks non auto-repeat rows on very tall grids with enough room for auto repetitions. 
+PASS Test that we don't crash when there are exactly kGridMaxTracks non auto-repeat columns on very wide grids with enough room for auto repetitions. 
+PASS Test that we don't crash when there are more than kGridMaxTracks non auto-repeat rows on very tall grids. 
+PASS Test that we don't crash when there are more than kGridMaxTracks non auto-repeat columns on very wide grids. 
+
diff --git a/LayoutTests/fast/css-grid-layout/grid-auto-repeat-huge-grid.html b/LayoutTests/fast/css-grid-layout/grid-auto-repeat-huge-grid.html
new file mode 100644 (file)
index 0000000..61b7e42
--- /dev/null
@@ -0,0 +1,333 @@
+<!DOCTYPE html>
+<title>Test for auto-fit and auto-fill with huge grids (lots of tracks)</title>
+<meta name="author" title="Sergio Villar Senin" href="mailto:svillar@igalia.com">
+<link href="resources/grid.css" rel="stylesheet">
+<link href="../css-intrinsic-dimensions/resources/width-keyword-classes.css" rel="stylesheet">
+<link href="../css-intrinsic-dimensions/resources/height-keyword-classes.css" rel="stylesheet">
+
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/grid-definitions-parsing-utils.js"></script>
+<style>
+ .wideGrid { width: 1000000000px; }
+ .tallGrid { height: 1000000000px; }
+
+ .width25k { width: 25000px; }
+ .height25k { height: 25000px; }
+
+ .lastColumn { grid-column: -2 / -1; }
+ .lastRow { grid-row: -2 / -1; }
+
+ .minSizeWideGrid { min-width: 1000000000px; }
+ .minSizeTallGrid { min-height: 1000000000px; }
+
+ .lotsOfFixedRepeatWithAutoFitCols { grid-template-columns: repeat(auto-fit, 10px 2px 8px) repeat(992, 1px); }
+ .lotsOfFixedRepeatWithAutoFillCols { grid-template-columns: repeat(auto-fill, 10px 2px 8px 7px 20px) repeat(995, 1px); }
+
+ .lotsOfAutoRepeatWithAutoFitCols { grid-template-columns: repeat(auto-fit, 10px 2px 8px) repeat(10, 1px); }
+ .lotsOfAutoRepeatWithAutoFillCols { grid-template-columns: repeat(auto-fill, 10px 2px 8px 7px 20px) repeat(10, 1px); }
+
+ .lotsOfFixedRepeatWithAutoFitColsReversed { grid-template-columns:  repeat(992, 1px) repeat(auto-fit, 10px 2px 8px); }
+ .lotsOfFixedRepeatWithAutoFillColsReversed { grid-template-columns:  repeat(995, 1px) repeat(auto-fill, 10px 2px 8px 7px 20px); }
+
+ .lotsOfFixedRepeatWithAutoFitRows { grid-template-rows: repeat(auto-fit, 10px 2px 8px) repeat(992, 1px); }
+ .lotsOfFixedRepeatWithAutoFillRows { grid-template-rows: repeat(auto-fill, 10px 2px 8px 7px 20px) repeat(995, 1px); }
+
+ .lotsOfAutoRepeatWithAutoFitRows { grid-template-rows: repeat(auto-fit, 10px 2px 8px) repeat(10, 1px); }
+ .lotsOfAutoRepeatWithAutoFillRows { grid-template-rows: repeat(auto-fill, 10px 2px 8px 7px 20px) repeat(10, 1px); }
+
+ .lotsOfFixedRepeatWithAutoFitRowsReversed { grid-template-rows:  repeat(992, 1px) repeat(auto-fit, 10px 2px 8px); }
+ .lotsOfFixedRepeatWithAutoFillRowsReversed { grid-template-rows: repeat(995, 1px) repeat(auto-fill, 10px 2px 8px 7px 20px); }
+
+ .autoFitRows25px { grid-template-rows: repeat(auto-fit, 10px 2px 8px 5px); }
+ .autoFitCols25px { grid-template-columns: repeat(auto-fit, 10px 2px 8px 5px); }
+
+ .autoFillRows25px { grid-template-rows: repeat(auto-fill, 17px 8px); }
+ .autoFillCols25px { grid-template-columns: repeat(auto-fill, 2px 23px); }
+
+ .autoFitRows205pxFixed5px { grid-template-rows: repeat(900, 5px) repeat(auto-fit, 20px 50px 13px 50px 72px); }
+ .autoFitCols205pxFixed5px { grid-template-columns: repeat(900, 5px) repeat(auto-fit, 20px 50px 13px 50px 72px); }
+
+ .autoFillRows205pxFixed5px { grid-template-rows:  repeat(900, 5px) repeat(auto-fill, 200px 5px); }
+ .autoFillCols205pxFixed5px { grid-template-columns: repeat(900, 5px) repeat(auto-fill, 200px 5px); }
+
+ .autoFitAndAThousandFixedRows { grid-template-rows: repeat(auto-fit, 20px 50px 72px) repeat(1000, 37px); }
+ .autoFitAndAThousandFixedCols { grid-template-columns: repeat(auto-fit, 20px 50px 72px) repeat(1000, 37px); }
+
+ .autoFillAndAThousandFixedRows { grid-template-rows: repeat(auto-fill, 2px) repeat(1000, 37px); }
+ .autoFillAndAThousandFixedCols { grid-template-columns: repeat(auto-fill, 2px) repeat(1000, 37px); }
+
+ .autoFitAndMoreThanThousandFixedRows { grid-template-rows:  repeat(700, 7px) repeat(auto-fit, 11px 13px 125px) repeat(600, 6px); }
+ .autoFitAndMoreThanThousandFixedCols { grid-template-columns:  repeat(700, 7px) repeat(auto-fit, 11px 13px 125px) repeat(600, 6px); }
+
+ .autoFillAndMoreThanThousandFixedRows { grid-template-rows: repeat(700, 7px) repeat(auto-fill, 20px 50px 72px) repeat(600, 6px); }
+ .autoFillAndMoreThanThousandFixedCols { grid-template-columns: repeat(700, 7px) repeat(auto-fill, 20px 50px 72px) repeat(600, 6px); }
+</style>
+
+<p>This test checks that we properly enforce the maximum number of grid tracks in different situations.</p>
+
+<div id="autoFillGridCols" class="grid"></div>
+<div id="autoFitGridCols" class="grid">
+    <div style="grid-column: 1;"></div>
+    <div style="grid-column: 2;"></div>
+    <div class="lastColumn"></div>
+</div>
+
+<div id="autoFillGridRows" class="grid"></div>
+<div id="autoFitGridRows" class="grid">
+    <div style="grid-row: 1;"></div>
+    <div style="grid-row: 2;"></div>
+    <div class="lastRow"></div>
+</div>
+
+<script>
+var maxTracksForTesting = 1000;  // Keep this value in sync with the ones used in CSS definitions.
+var autoFillGridElementCols = document.getElementById("autoFillGridCols");
+var autoFitGridElementCols = document.getElementById("autoFitGridCols");
+var autoFillGridElementRows = document.getElementById("autoFillGridRows");
+var autoFitGridElementRows = document.getElementById("autoFitGridRows");
+if (window.internals)
+    window.internals.setGridMaxTracksLimit(maxTracksForTesting);
+
+function getTracksCheckingLength(gridElement, property, length, classNames) {
+    for (let className of classNames)
+        gridElement.classList.add(className);
+
+    var propertyValue = getComputedStyle(gridElement, '').getPropertyValue(property);
+    for (let className of classNames)
+        gridElement.classList.remove(className);
+
+    if (propertyValue == "") {
+        assert_equals(length, 0);
+        return [];
+    }
+
+    var tracks = propertyValue.split(' ');
+    assert_equals(tracks.length, length);
+    return tracks;
+}
+
+test(function() {
+    var autoFillGrid = getTracksCheckingLength(autoFillGridElementCols, "grid-template-columns", maxTracksForTesting, ["wideGrid", "lotsOfFixedRepeatWithAutoFillCols"]);
+    var autoFitGrid = getTracksCheckingLength(autoFitGridElementCols, "grid-template-columns", maxTracksForTesting, ["wideGrid", "lotsOfFixedRepeatWithAutoFitCols"]);
+    assert_equals(autoFitGrid[maxTracksForTesting - 1], "10px");
+    assert_equals(autoFitGrid[maxTracksForTesting - 2], "0px");
+    assert_equals(autoFitGrid[0], "10px");
+    assert_equals(autoFitGrid[1], "2px");
+
+    assert_equals(autoFillGrid[maxTracksForTesting - 1], "20px");
+    assert_equals(autoFillGrid[maxTracksForTesting - 2], "7px");
+    assert_equals(autoFillGrid[0], "10px");
+    assert_equals(autoFillGrid[1], "2px");
+}, "Test that we don't get more than kGridMaxTracks repetitions even on very wide grids (normal tracks clamped).");
+
+test(function() {
+    var autoFillGrid = getTracksCheckingLength(autoFillGridElementRows, "grid-template-rows", maxTracksForTesting, ["tallGrid", "lotsOfFixedRepeatWithAutoFillRows"]);
+    var autoFitGrid = getTracksCheckingLength(autoFitGridElementRows, "grid-template-rows", maxTracksForTesting, ["tallGrid", "lotsOfFixedRepeatWithAutoFitRows"]);
+    assert_equals(autoFitGrid[maxTracksForTesting - 1], "10px");
+    assert_equals(autoFitGrid[maxTracksForTesting - 2], "0px");
+    assert_equals(autoFitGrid[0], "10px");
+    assert_equals(autoFitGrid[1], "2px");
+
+    assert_equals(autoFillGrid[maxTracksForTesting - 1], "20px");
+    assert_equals(autoFillGrid[maxTracksForTesting - 2], "7px");
+    assert_equals(autoFillGrid[0], "10px");
+    assert_equals(autoFillGrid[1], "2px");
+}, "Test that we don't get more than kGridMaxTracks repetitions even on very tall grids (normal tracks clamped).");
+
+test(function() {
+    var autoFillGrid = getTracksCheckingLength(autoFillGridElementCols, "grid-template-columns", maxTracksForTesting, ["wideGrid", "lotsOfFixedRepeatWithAutoFillColsReversed"]);
+    var autoFitGrid = getTracksCheckingLength(autoFitGridElementCols, "grid-template-columns", maxTracksForTesting, ["wideGrid", "lotsOfFixedRepeatWithAutoFitColsReversed"]);
+
+    assert_equals(autoFitGrid[maxTracksForTesting - 1], "2px");
+    assert_equals(autoFitGrid[maxTracksForTesting - 2], "0px");
+    assert_equals(autoFitGrid[0], "1px");
+    assert_equals(autoFitGrid[1], "1px");
+
+    assert_equals(autoFillGrid[maxTracksForTesting - 1], "20px");
+    assert_equals(autoFillGrid[maxTracksForTesting - 2], "7px");
+    assert_equals(autoFillGrid[0], "1px");
+    assert_equals(autoFillGrid[1], "1px");
+}, "Test that we don't get more than kGridMaxTracks repetitions even on very wide grids (auto repeat tracks clamped).");
+
+test(function() {
+    var autoFillGrid = getTracksCheckingLength(autoFillGridElementRows, "grid-template-rows", maxTracksForTesting, ["tallGrid", "lotsOfFixedRepeatWithAutoFillRowsReversed"]);
+    var autoFitGrid = getTracksCheckingLength(autoFitGridElementRows, "grid-template-rows", maxTracksForTesting, ["tallGrid", "lotsOfFixedRepeatWithAutoFitRowsReversed"]);
+
+    assert_equals(autoFitGrid[maxTracksForTesting - 1], "2px");
+    assert_equals(autoFitGrid[maxTracksForTesting - 2], "0px");
+    assert_equals(autoFitGrid[0], "1px");
+    assert_equals(autoFitGrid[1], "1px");
+
+    assert_equals(autoFillGrid[maxTracksForTesting - 1], "20px");
+    assert_equals(autoFillGrid[maxTracksForTesting - 2], "7px");
+    assert_equals(autoFillGrid[0], "1px");
+    assert_equals(autoFillGrid[1], "1px");
+}, "Test that we don't get more than kGridMaxTracks repetitions even on very tall grids (auto repeat tracks clamped).");
+
+test(function() {
+    autoFillGridElementCols.style.gridGap = "100px";
+    autoFitGridElementCols.style.gridGap = "100px";
+
+    var autoFillGrid = getTracksCheckingLength(autoFillGridElementCols, "grid-template-columns", maxTracksForTesting, ["wideGrid", "lotsOfAutoRepeatWithAutoFillCols"]);
+    var autoFitGrid = getTracksCheckingLength(autoFitGridElementCols, "grid-template-columns", maxTracksForTesting, ["wideGrid", "lotsOfAutoRepeatWithAutoFitCols"]);
+
+    assert_equals(autoFitGrid[maxTracksForTesting - 1], "10px");
+    assert_equals(autoFitGrid[maxTracksForTesting - 2], "0px");
+    assert_equals(autoFillGrid[maxTracksForTesting - 1], "20px");
+    assert_equals(autoFillGrid[maxTracksForTesting - 2], "7px");
+
+    autoFillGridElementCols.style.gridGap = "1000000px";
+    autoFitGridElementCols.style.gridGap = "1000000px";
+
+    getTracksCheckingLength(autoFillGridElementCols, "grid-template-columns", 130, ["wideGrid", "lotsOfAutoRepeatWithAutoFillCols"]);
+    getTracksCheckingLength(autoFitGridElementCols, "grid-template-columns", 82, ["wideGrid", "lotsOfAutoRepeatWithAutoFitCols"]);
+
+    autoFillGridElementCols.style.gridGap = "0px";
+    autoFitGridElementCols.style.gridGap = "0px";
+}, "Test that we don't get more than kGridMaxTracks repetitions even on very wide grids with gaps.");
+
+test(function() {
+    autoFillGridElementRows.style.gridGap = "100px";
+    autoFitGridElementRows.style.gridGap = "100px";
+
+    var autoFillGrid = getTracksCheckingLength(autoFillGridElementRows, "grid-template-rows", maxTracksForTesting, ["tallGrid", "lotsOfAutoRepeatWithAutoFillRows"]);
+    var autoFitGrid = getTracksCheckingLength(autoFitGridElementRows, "grid-template-rows", maxTracksForTesting, ["tallGrid", "lotsOfAutoRepeatWithAutoFitRows"]);
+
+    assert_equals(autoFitGrid[maxTracksForTesting - 1], "10px");
+    assert_equals(autoFitGrid[maxTracksForTesting - 2], "0px");
+    assert_equals(autoFillGrid[maxTracksForTesting - 1], "20px");
+    assert_equals(autoFillGrid[maxTracksForTesting - 2], "7px");
+
+    autoFillGridElementRows.style.gridGap = "1000000px";
+    autoFitGridElementRows.style.gridGap = "1000000px";
+
+    getTracksCheckingLength(autoFillGridElementRows, "grid-template-rows", 130, ["tallGrid", "lotsOfAutoRepeatWithAutoFillRows"]);
+    getTracksCheckingLength(autoFitGridElementRows, "grid-template-rows", 82, ["tallGrid", "lotsOfAutoRepeatWithAutoFitRows"]);
+
+    autoFillGridElementRows.style.gridGap = "0px";
+    autoFitGridElementRows.style.gridGap = "0px";
+}, "Test that we don't get more than kGridMaxTracks repetitions even on very tall grids with gaps.");
+
+test(function() {
+    var autoFillCols = getTracksCheckingLength(autoFillGridElementCols, "grid-template-columns", maxTracksForTesting, ["lotsOfAutoRepeatWithAutoFillCols", "minSizeWideGrid", "min-content"]);
+    var autoFitCols = getTracksCheckingLength(autoFitGridElementCols, "grid-template-columns", maxTracksForTesting, ["lotsOfAutoRepeatWithAutoFitCols", "minSizeWideGrid", "min-content"]);
+
+    // Check that clamping auto repetitions does not reduce the amount of the other tracks.
+    assert_equals(autoFillCols[maxTracksForTesting - 1], "20px");
+    assert_equals(autoFillCols[maxTracksForTesting - 2], "7px");
+    assert_equals(autoFitCols[maxTracksForTesting - 1], "10px");
+    assert_equals(autoFitCols[maxTracksForTesting - 2], "0px");
+}, "Test that we don't get more than kGridMaxTracks repetitions even on very wide grids with gaps and min-width.");
+
+test(function() {
+    var autoFillRows = getTracksCheckingLength(autoFillGridElementRows, "grid-template-rows", maxTracksForTesting, ["lotsOfAutoRepeatWithAutoFillRows", "minSizeTallGrid", "min-content"]);
+    var autoFitRows = getTracksCheckingLength(autoFitGridElementRows, "grid-template-rows", maxTracksForTesting, ["lotsOfAutoRepeatWithAutoFitRows", "minSizeTallGrid", "min-content"]);
+
+    assert_equals(autoFillRows[maxTracksForTesting - 1], "20px");
+    assert_equals(autoFillRows[maxTracksForTesting - 2], "7px");
+    assert_equals(autoFitRows[maxTracksForTesting - 1], "10px");
+    assert_equals(autoFitRows[maxTracksForTesting - 2], "0px");
+}, "Test that we don't get more than kGridMaxTracks repetitions even on very tall grids with gaps and min-height.");
+
+test(function() {
+    var autoFillRows = getTracksCheckingLength(autoFillGridElementRows, "grid-template-rows", maxTracksForTesting, ["height25k", "autoFillRows25px"]);
+    var autoFitRows = getTracksCheckingLength(autoFitGridElementRows, "grid-template-rows", maxTracksForTesting, ["height25k", "autoFitRows25px"]);
+
+    assert_equals(autoFillRows[maxTracksForTesting - 1], "8px");
+    assert_equals(autoFillRows[maxTracksForTesting - 2], "17px");
+    assert_equals(autoFitRows[maxTracksForTesting - 1], "5px");
+    assert_equals(autoFitRows[maxTracksForTesting - 2], "0px");
+}, "Test that we don't crash when there are exactly kGridMaxTracks auto repeat rows on very tall grids.");
+
+test(function() {
+    var autoFillCols = getTracksCheckingLength(autoFillGridElementCols, "grid-template-columns", maxTracksForTesting, ["width25k", "autoFillCols25px"]);
+    var autoFitCols = getTracksCheckingLength(autoFitGridElementCols, "grid-template-columns", maxTracksForTesting, ["width25k", "autoFitCols25px"]);
+
+    assert_equals(autoFillCols[maxTracksForTesting - 1], "23px");
+    assert_equals(autoFillCols[maxTracksForTesting - 2], "2px");
+    assert_equals(autoFitCols[maxTracksForTesting - 1], "5px");
+    assert_equals(autoFitCols[maxTracksForTesting - 2], "0px");
+}, "Test that we don't crash when there are exactly kGridMaxTracks auto repeat columns on very wide grids.");
+
+test(function() {
+    var autoFillRows = getTracksCheckingLength(autoFillGridElementRows, "grid-template-rows", maxTracksForTesting, ["height25k", "autoFillRows205pxFixed5px"]);
+    var autoFitRows = getTracksCheckingLength(autoFitGridElementRows, "grid-template-rows", maxTracksForTesting, ["height25k", "autoFitRows205pxFixed5px"]);
+
+    assert_equals(autoFillRows[maxTracksForTesting - 1], "5px");
+    assert_equals(autoFillRows[maxTracksForTesting - 2], "200px");
+    assert_equals(autoFitRows[maxTracksForTesting - 1], "72px");
+    assert_equals(autoFitRows[maxTracksForTesting - 2], "0px");
+}, "Test that we don't crash when there are exactly kGridMaxTracks (normal and auto-repeat) rows on very tall grids.");
+
+test(function() {
+    var autoFillCols = getTracksCheckingLength(autoFillGridElementCols, "grid-template-columns", maxTracksForTesting, ["width25k", "autoFillCols205pxFixed5px"]);
+    var autoFitCols = getTracksCheckingLength(autoFitGridElementCols, "grid-template-columns", maxTracksForTesting, ["width25k", "autoFitCols205pxFixed5px"]);
+
+    assert_equals(autoFillCols[maxTracksForTesting - 1], "5px");
+    assert_equals(autoFillCols[maxTracksForTesting - 2], "200px");
+    assert_equals(autoFitCols[maxTracksForTesting - 1], "72px");
+    assert_equals(autoFitCols[maxTracksForTesting - 2], "0px");
+}, "Test that we don't crash when there are exactly kGridMaxTracks (normal and auto-repeat) columns on very wide grids.");
+
+test(function() {
+    var autoFillGrid = getTracksCheckingLength(autoFillGridElementRows, "grid-template-rows", maxTracksForTesting, ["height25k", "autoFillAndAThousandFixedRows"]);
+    var autoFitGrid = getTracksCheckingLength(autoFitGridElementRows, "grid-template-rows", maxTracksForTesting, ["height25k", "autoFitAndAThousandFixedRows"]);
+
+    assert_equals(autoFillGrid[maxTracksForTesting - 1], "37px");
+    assert_equals(autoFillGrid[0], "2px");
+    assert_equals(autoFitGrid[maxTracksForTesting - 1], "37px");
+    assert_equals(autoFitGrid[0], "20px");
+}, "Test that we don't crash when there are exactly kGridMaxTracks non auto-repeat rows on very tall grids.");
+
+test(function() {
+    var autoFillGrid = getTracksCheckingLength(autoFillGridElementCols, "grid-template-columns", maxTracksForTesting, ["width25k", "autoFillAndAThousandFixedCols"]);
+    var autoFitGrid = getTracksCheckingLength(autoFitGridElementCols, "grid-template-columns", maxTracksForTesting, ["width25k", "autoFitAndAThousandFixedCols"]);
+
+    assert_equals(autoFillGrid[maxTracksForTesting - 1], "37px");
+    assert_equals(autoFillGrid[0], "2px");
+    assert_equals(autoFitGrid[maxTracksForTesting - 1], "37px");
+    assert_equals(autoFitGrid[0], "20px");
+}, "Test that we don't crash when there are exactly kGridMaxTracks non auto-repeat columns on very wide grids.");
+
+test(function() {
+    var autoFillGrid = getTracksCheckingLength(autoFillGridElementRows, "grid-template-rows", maxTracksForTesting, ["tallGrid", "autoFillAndAThousandFixedRows"]);
+    var autoFitGrid = getTracksCheckingLength(autoFitGridElementRows, "grid-template-rows", maxTracksForTesting, ["tallGrid", "autoFitAndAThousandFixedRows"]);
+
+    assert_equals(autoFillGrid[maxTracksForTesting - 1], "2px");
+    assert_equals(autoFillGrid[0], "2px");
+    assert_equals(autoFitGrid[maxTracksForTesting - 1], "20px");
+    assert_equals(autoFitGrid[0], "20px");
+}, "Test that we don't crash when there are exactly kGridMaxTracks non auto-repeat rows on very tall grids with enough room for auto repetitions.");
+
+test(function() {
+    var autoFillGrid = getTracksCheckingLength(autoFillGridElementCols, "grid-template-columns", maxTracksForTesting, ["wideGrid", "autoFillAndAThousandFixedCols"]);
+    var autoFitGrid = getTracksCheckingLength(autoFitGridElementCols, "grid-template-columns", maxTracksForTesting, ["wideGrid", "autoFitAndAThousandFixedCols"]);
+
+    assert_equals(autoFillGrid[maxTracksForTesting - 1], "2px");
+    assert_equals(autoFillGrid[0], "2px");
+    assert_equals(autoFitGrid[maxTracksForTesting - 1], "20px");
+    assert_equals(autoFitGrid[0], "20px");
+}, "Test that we don't crash when there are exactly kGridMaxTracks non auto-repeat columns on very wide grids with enough room for auto repetitions.");
+
+test(function() {
+    var autoFillGrid = getTracksCheckingLength(autoFillGridElementRows, "grid-template-rows", maxTracksForTesting, ["height25k", "autoFillAndMoreThanThousandFixedRows"]);
+    var autoFitGrid = getTracksCheckingLength(autoFitGridElementRows, "grid-template-rows", maxTracksForTesting, ["height25k", "autoFitAndMoreThanThousandFixedRows"]);
+
+    assert_equals(autoFillGrid[maxTracksForTesting - 1], "72px");
+    assert_equals(autoFillGrid[0], "7px");
+    assert_equals(autoFitGrid[maxTracksForTesting - 1], "125px");
+    assert_equals(autoFitGrid[0], "7px");
+}, "Test that we don't crash when there are more than kGridMaxTracks non auto-repeat rows on very tall grids.");
+
+test(function() {
+    var autoFillGrid = getTracksCheckingLength(autoFillGridElementCols, "grid-template-columns", maxTracksForTesting, ["width25k", "autoFillAndMoreThanThousandFixedCols"]);
+    var autoFitGrid = getTracksCheckingLength(autoFitGridElementCols, "grid-template-columns", maxTracksForTesting, ["width25k", "autoFitAndMoreThanThousandFixedCols"]);
+
+    assert_equals(autoFillGrid[maxTracksForTesting - 1], "72px");
+    assert_equals(autoFillGrid[0], "7px");
+    assert_equals(autoFitGrid[maxTracksForTesting - 1], "125px");
+    assert_equals(autoFitGrid[0], "7px");
+}, "Test that we don't crash when there are more than kGridMaxTracks non auto-repeat columns on very wide grids.");
+
+</script>
index af3e386..46d0e49 100644 (file)
@@ -2624,6 +2624,7 @@ set(WebCore_SOURCES
     rendering/style/ContentData.cpp
     rendering/style/CounterDirectives.cpp
     rendering/style/FillLayer.cpp
+    rendering/style/GridPosition.cpp
     rendering/style/GridPositionsResolver.cpp
     rendering/style/KeyframeList.cpp
     rendering/style/NinePieceImage.cpp
index c4f736b..bb1fe8a 100644 (file)
@@ -1,3 +1,73 @@
+2017-03-27  Sergio Villar Senin  <svillar@igalia.com>
+
+        [css-grid] Clamp the number of autorepeat tracks
+        https://bugs.webkit.org/show_bug.cgi?id=170120
+
+        Reviewed by Manuel Rego Casasnovas.
+
+        As suggested by the specs we do clamp the maximum number of tracks per grid in order to
+        minimize potential OOM situations. However we were not considering the case of the recently
+        added auto repeat syntax. Abnormally huge values for the width/height on the grid container
+        could lead to a number of auto repeat tracks higher than the maximum.
+
+        A new API was added to Internals in order to test limits without having to create huge
+        grids. This new API allows clients to set an arbitrary limit to the number of tracks. The
+        addition of this new API forced us to add GridPosition.cpp to the project to define the
+        global variable we use for testing. We took the chance to move part of the implementation
+        from the header file to the source file.
+
+        Last but not least, several new ASSERTs were added to Grid.cpp implementation to ensure that
+        we do not surpass the grid track limits.
+
+        Test: fast/css-grid-layout/grid-auto-repeat-huge-grid.html
+
+        * CMakeLists.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * css/parser/CSSPropertyParser.cpp:
+        (WebCore::consumeGridTrackRepeatFunction):
+        * rendering/Grid.cpp:
+        (WebCore::Grid::ensureGridSize): Added ASSERT.
+        (WebCore::Grid::setSmallestTracksStart): Ditto.
+        (WebCore::Grid::setAutoRepeatTracks): Ditto.
+        (WebCore::Grid::setAutoRepeatEmptyColumns): Ditto.
+        (WebCore::Grid::setAutoRepeatEmptyRows): Ditto.
+        * rendering/RenderGrid.cpp:
+        (WebCore::RenderGrid::clampAutoRepeatTracks): New method.
+        (WebCore::RenderGrid::placeItemsOnGrid): Clamp the number of auto repeat tracks before
+        passing them to the Grid.
+        * rendering/RenderGrid.h:
+        * rendering/style/GridArea.h:
+        (WebCore::GridSpan::GridSpan):
+        * rendering/style/GridPosition.cpp: Added.
+        (WebCore::GridPosition::setExplicitPosition):
+        (WebCore::GridPosition::setAutoPosition):
+        (WebCore::GridPosition::setSpanPosition):
+        (WebCore::GridPosition::setNamedGridArea):
+        (WebCore::GridPosition::integerPosition):
+        (WebCore::GridPosition::namedGridLine):
+        (WebCore::GridPosition::spanPosition):
+        (WebCore::GridPosition::operator==):
+        * rendering/style/GridPosition.h:
+        (WebCore::GridPosition::shouldBeResolvedAgainstOppositePosition):
+        (WebCore::GridPosition::max):
+        (WebCore::GridPosition::min):
+        (WebCore::GridPosition::setMaxPositionForTesting):
+        (WebCore::GridPosition::setExplicitPosition): Deleted.
+        (WebCore::GridPosition::setAutoPosition): Deleted.
+        (WebCore::GridPosition::setSpanPosition): Deleted.
+        (WebCore::GridPosition::setNamedGridArea): Deleted.
+        (WebCore::GridPosition::integerPosition): Deleted.
+        (WebCore::GridPosition::namedGridLine): Deleted.
+        (WebCore::GridPosition::spanPosition): Deleted.
+        (WebCore::GridPosition::operator==): Deleted.
+        * rendering/style/GridPositionsResolver.cpp:
+        (WebCore::GridPositionsResolver::explicitGridColumnCount):
+        (WebCore::GridPositionsResolver::explicitGridRowCount):
+        * testing/Internals.cpp:
+        (WebCore::Internals::setGridMaxTracksLimit):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2017-03-29  Ryosuke Niwa  <rniwa@webkit.org>
 
         Disconnecting a HTMLObjectElement does not always unload its content document
index 2a4aa8e..5e8761e 100644 (file)
                E172AF70180F289500FBADB9 /* CryptoKeyUsage.h in Headers */ = {isa = PBXBuildFile; fileRef = E172AF6F180F289500FBADB9 /* CryptoKeyUsage.h */; };
                E172AF8F1811BC3700FBADB9 /* JSDOMPromise.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E172AF8D1811BC3700FBADB9 /* JSDOMPromise.cpp */; };
                E172AF901811BC3700FBADB9 /* JSDOMPromise.h in Headers */ = {isa = PBXBuildFile; fileRef = E172AF8E1811BC3700FBADB9 /* JSDOMPromise.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               E175BA991E8AE3380071454F /* GridPosition.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1F817451E897A5200F22CD0 /* GridPosition.cpp */; };
                E179F0DA1B9774FE00ED0A27 /* Internals.mm in Sources */ = {isa = PBXBuildFile; fileRef = E179F0D91B9774FE00ED0A27 /* Internals.mm */; };
                E17B491516A9B094001C8839 /* TransitionEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E17B491316A9B093001C8839 /* TransitionEvent.cpp */; };
                E17B491616A9B094001C8839 /* TransitionEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = E17B491416A9B093001C8839 /* TransitionEvent.h */; };
                E1F80B8618317252007885C3 /* CryptoKeyPair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoKeyPair.h; sourceTree = "<group>"; };
                E1F80B8B183172B5007885C3 /* JSCryptoKeyPair.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSCryptoKeyPair.cpp; sourceTree = "<group>"; };
                E1F80B8C183172B5007885C3 /* JSCryptoKeyPair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCryptoKeyPair.h; sourceTree = "<group>"; };
+               E1F817451E897A5200F22CD0 /* GridPosition.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GridPosition.cpp; sourceTree = "<group>"; };
                E1FE1368183FE1AB00892F13 /* CryptoAlgorithmRSA_OAEP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CryptoAlgorithmRSA_OAEP.cpp; sourceTree = "<group>"; };
                E1FE1369183FE1AB00892F13 /* CryptoAlgorithmRSA_OAEP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoAlgorithmRSA_OAEP.h; sourceTree = "<group>"; };
                E1FE136E183FECF000892F13 /* CryptoAlgorithmRSA_OAEPMac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CryptoAlgorithmRSA_OAEPMac.cpp; sourceTree = "<group>"; };
                                BC5EB69D0E81DAEB00B25965 /* FillLayer.h */,
                                CD3E251B18046B0600E27F56 /* GridArea.h */,
                                CDEF4FD617E85C8F00AEE24B /* GridLength.h */,
+                               E1F817451E897A5200F22CD0 /* GridPosition.cpp */,
                                A12705C21656BD6500C2E27C /* GridPosition.h */,
                                CDF7483C18FEBCEC0006ECC0 /* GridPositionsResolver.cpp */,
                                CDF7483D18FEBCEC0006ECC0 /* GridPositionsResolver.h */,
                                0FA24D79162DF91900A3F4C0 /* GraphicsLayerUpdater.cpp in Sources */,
                                B2A015AA0AF6CD53006BCE0E /* GraphicsTypes.cpp in Sources */,
                                E112F4711E3A861200D6CDFD /* Grid.cpp in Sources */,
+                               E175BA991E8AE3380071454F /* GridPosition.cpp in Sources */,
                                CDF7483E18FEBCEC0006ECC0 /* GridPositionsResolver.cpp in Sources */,
                                316DCB4B1E7910A6001B5F87 /* JSRTCIceConnectionState.cpp in Sources */,
                                E12DE7171E4B749C00F9ACCF /* GridTrackSizingAlgorithm.cpp in Sources */,
index 725efd9..7f95c6f 100644 (file)
@@ -3332,7 +3332,7 @@ static bool consumeGridTrackRepeatFunction(CSSParserTokenRange& range, CSSParser
         RefPtr<CSSPrimitiveValue> repetition = consumePositiveInteger(args);
         if (!repetition)
             return false;
-        repetitions = clampTo<size_t>(repetition->doubleValue(), 0, kGridMaxTracks);
+        repetitions = clampTo<size_t>(repetition->doubleValue(), 0, GridPosition::max());
         repeatedValues = CSSValueList::createSpaceSeparated();
     }
     if (!consumeCommaIncludingWhitespace(args))
@@ -3362,7 +3362,7 @@ static bool consumeGridTrackRepeatFunction(CSSParserTokenRange& range, CSSParser
         list.append(repeatedValues.releaseNonNull());
     else {
         // We clamp the repetitions to a multiple of the repeat() track list's size, while staying below the max grid size.
-        repetitions = std::min(repetitions, kGridMaxTracks / numberOfTracks);
+        repetitions = std::min(repetitions, GridPosition::max() / numberOfTracks);
         for (size_t i = 0; i < repetitions; ++i) {
             for (size_t j = 0; j < repeatedValues->length(); ++j)
                 list.append(*repeatedValues->itemWithoutBoundsCheck(j));
index 74838b9..0a118de 100644 (file)
@@ -45,6 +45,8 @@ unsigned Grid::numTracks(GridTrackSizingDirection direction) const
 
 void Grid::ensureGridSize(unsigned maximumRowSize, unsigned maximumColumnSize)
 {
+    ASSERT(static_cast<int>(maximumRowSize) < GridPosition::max() * 2);
+    ASSERT(static_cast<int>(maximumColumnSize) < GridPosition::max() * 2);
     const size_t oldColumnSize = numTracks(ForColumns);
     const size_t oldRowSize = numTracks(ForRows);
     if (maximumRowSize > oldRowSize) {
@@ -74,6 +76,8 @@ void Grid::insert(RenderBox& child, const GridArea& area)
 
 void Grid::setSmallestTracksStart(int rowStart, int columnStart)
 {
+    ASSERT(rowStart > GridPosition::min() && rowStart < GridPosition::max() - 1);
+    ASSERT(columnStart > GridPosition::min() && columnStart < GridPosition::max() - 1);
     m_smallestRowStart = rowStart;
     m_smallestColumnStart = columnStart;
 }
@@ -96,6 +100,8 @@ void Grid::setGridItemArea(const RenderBox& item, GridArea area)
 
 void Grid::setAutoRepeatTracks(unsigned autoRepeatRows, unsigned autoRepeatColumns)
 {
+    ASSERT(static_cast<unsigned>(GridPosition::max()) >= numTracks(ForRows) + autoRepeatRows);
+    ASSERT(static_cast<unsigned>(GridPosition::max()) >= numTracks(ForColumns) + autoRepeatColumns);
     m_autoRepeatRows = autoRepeatRows;
     m_autoRepeatColumns =  autoRepeatColumns;
 }
@@ -107,11 +113,13 @@ unsigned Grid::autoRepeatTracks(GridTrackSizingDirection direction) const
 
 void Grid::setAutoRepeatEmptyColumns(std::unique_ptr<OrderedTrackIndexSet> autoRepeatEmptyColumns)
 {
+    ASSERT(!autoRepeatEmptyColumns || (autoRepeatEmptyColumns->size() <= m_autoRepeatColumns));
     m_autoRepeatEmptyColumns = WTFMove(autoRepeatEmptyColumns);
 }
 
 void Grid::setAutoRepeatEmptyRows(std::unique_ptr<OrderedTrackIndexSet> autoRepeatEmptyRows)
 {
+    ASSERT(!autoRepeatEmptyRows || (autoRepeatEmptyRows->size() <= m_autoRepeatRows));
     m_autoRepeatEmptyRows = WTFMove(autoRepeatEmptyRows);
 }
 
index 5af068a..9656071 100644 (file)
@@ -535,10 +535,31 @@ std::unique_ptr<OrderedTrackIndexSet> RenderGrid::computeEmptyTracksForAutoRepea
     return emptyTrackIndexes;
 }
 
+unsigned RenderGrid::clampAutoRepeatTracks(GridTrackSizingDirection direction, unsigned autoRepeatTracks) const
+{
+    if (!autoRepeatTracks)
+        return 0;
+
+    unsigned insertionPoint = direction == ForColumns ? style().gridAutoRepeatColumnsInsertionPoint() : style().gridAutoRepeatRowsInsertionPoint();
+    unsigned maxTracks = static_cast<unsigned>(GridPosition::max());
+
+    if (!insertionPoint)
+        return std::min(autoRepeatTracks, maxTracks);
+
+    if (insertionPoint >= maxTracks)
+        return 0;
+
+    return std::min(autoRepeatTracks, maxTracks - insertionPoint);
+}
+
 void RenderGrid::placeItemsOnGrid(Grid& grid, SizingOperation sizingOperation) const
 {
     unsigned autoRepeatColumns = computeAutoRepeatTracksCount(ForColumns, sizingOperation);
     unsigned autoRepeatRows = computeAutoRepeatTracksCount(ForRows, sizingOperation);
+
+    autoRepeatRows = clampAutoRepeatTracks(ForRows, autoRepeatRows);
+    autoRepeatColumns = clampAutoRepeatTracks(ForColumns, autoRepeatColumns);
+
     if (autoRepeatColumns != grid.autoRepeatTracks(ForColumns) || autoRepeatRows != grid.autoRepeatTracks(ForRows)) {
         grid.setNeedsItemsPlacement(true);
         grid.setAutoRepeatTracks(autoRepeatRows, autoRepeatColumns);
index 5faf435..bcdf2b0 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2011 Apple Inc. All rights reserved.
- * Copyright (C) 2013, 2014 Igalia S.L.
+ * Copyright (C) 2013-2017 Igalia S.L.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -63,7 +63,7 @@ public:
     // Required by GridTrackSizingAlgorithm. Keep them under control.
     bool isOrthogonalChild(const RenderBox&) const;
     LayoutUnit guttersSize(const Grid&, GridTrackSizingDirection, unsigned startLine, unsigned span, SizingOperation) const;
-    
+
 protected:
     ItemPosition selfAlignmentNormalBehavior(const RenderBox* child = nullptr) const override
     {
@@ -86,6 +86,8 @@ private:
 
     unsigned computeAutoRepeatTracksCount(GridTrackSizingDirection, SizingOperation) const;
 
+    unsigned clampAutoRepeatTracks(GridTrackSizingDirection, unsigned autoRepeatTracks) const;
+
     std::unique_ptr<OrderedTrackIndexSet> computeEmptyTracksForAutoRepeat(Grid&, GridTrackSizingDirection) const;
 
     void placeItemsOnGrid(Grid&, SizingOperation) const;
index 082620f..73ded3f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2013 Google Inc. All rights reserved.
- * Copyright (C) 2013, 2014, 2016 Igalia S.L.
+ * Copyright (C) 2013-2017 Igalia S.L.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -156,8 +156,8 @@ private:
         }
 #endif
 
-        m_startLine = std::max(-kGridMaxTracks, std::min(startLine, kGridMaxTracks - 1));
-        m_endLine = std::max(-kGridMaxTracks + 1, std::min(endLine, kGridMaxTracks));
+        m_startLine = std::max(GridPosition::min(), std::min(startLine, GridPosition::max() - 1));
+        m_endLine = std::max(GridPosition::min() + 1, std::min(endLine, GridPosition::max()));
     }
 
     int m_startLine;
diff --git a/Source/WebCore/rendering/style/GridPosition.cpp b/Source/WebCore/rendering/style/GridPosition.cpp
new file mode 100644 (file)
index 0000000..d596736
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "GridPosition.h"
+
+namespace WebCore {
+
+std::optional<int> GridPosition::gMaxPositionForTesting;
+static const int kGridMaxPosition = 1000000;
+
+void GridPosition::setExplicitPosition(int position, const String& namedGridLine)
+{
+    m_type = ExplicitPosition;
+    setIntegerPosition(position);
+    m_namedGridLine = namedGridLine;
+}
+
+void GridPosition::setAutoPosition()
+{
+    m_type = AutoPosition;
+    m_integerPosition = 0;
+}
+
+// 'span' values cannot be negative, yet we reuse the <integer> position which can
+// be. This means that we have to convert the span position to an integer, losing
+// some precision here. It shouldn't be an issue in practice though.
+void GridPosition::setSpanPosition(int position, const String& namedGridLine)
+{
+    m_type = SpanPosition;
+    setIntegerPosition(position);
+    m_namedGridLine = namedGridLine;
+}
+
+void GridPosition::setNamedGridArea(const String& namedGridArea)
+{
+    m_type = NamedGridAreaPosition;
+    m_namedGridLine = namedGridArea;
+}
+
+int GridPosition::integerPosition() const
+{
+    ASSERT(type() == ExplicitPosition);
+    return m_integerPosition;
+}
+
+String GridPosition::namedGridLine() const
+{
+    ASSERT(type() == ExplicitPosition || type() == SpanPosition || type() == NamedGridAreaPosition);
+    return m_namedGridLine;
+}
+
+int GridPosition::spanPosition() const
+{
+    ASSERT(type() == SpanPosition);
+    return m_integerPosition;
+}
+
+int GridPosition::max()
+{
+    return gMaxPositionForTesting.value_or(kGridMaxPosition);
+}
+
+int GridPosition::min()
+{
+    return -max();
+}
+
+bool GridPosition::operator==(const GridPosition& other) const
+{
+    return m_type == other.m_type && m_integerPosition == other.m_integerPosition && m_namedGridLine == other.m_namedGridLine;
+}
+
+void GridPosition::setMaxPositionForTesting(unsigned maxPosition)
+{
+    gMaxPositionForTesting = static_cast<int>(maxPosition);
+}
+
+} // namespace WebCore
index 7190c4c..a39f4d8 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2012 Google Inc. All rights reserved.
- * Copyright (C) 2013, 2014 Igalia S.L.
+ * Copyright (C) 2013-2017 Igalia S.L.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -35,9 +35,6 @@
 
 namespace WebCore {
 
-// Recommended maximum size for both explicit and implicit grids.
-const int kGridMaxTracks = 1000000;
-
 enum GridPositionType {
     AutoPosition,
     ExplicitPosition, // [ <integer> || <string> ]
@@ -67,68 +64,30 @@ public:
     bool isSpan() const { return m_type == SpanPosition; }
     bool isNamedGridArea() const { return m_type == NamedGridAreaPosition; }
 
-    void setExplicitPosition(int position, const String& namedGridLine)
-    {
-        m_type = ExplicitPosition;
-        setIntegerPosition(position);
-        m_namedGridLine = namedGridLine;
-    }
+    WEBCORE_EXPORT void setExplicitPosition(int position, const String& namedGridLine);
+    void setAutoPosition();
+    WEBCORE_EXPORT void setSpanPosition(int position, const String& namedGridLine);
+    void setNamedGridArea(const String&);
 
-    void setAutoPosition()
-    {
-        m_type = AutoPosition;
-        m_integerPosition = 0;
-    }
+    WEBCORE_EXPORT int integerPosition() const;
+    String namedGridLine() const;
+    WEBCORE_EXPORT int spanPosition() const;
 
-    // 'span' values cannot be negative, yet we reuse the <integer> position which can
-    // be. This means that we have to convert the span position to an integer, losing
-    // some precision here. It shouldn't be an issue in practice though.
-    void setSpanPosition(int position, const String& namedGridLine)
-    {
-        m_type = SpanPosition;
-        setIntegerPosition(position);
-        m_namedGridLine = namedGridLine;
-    }
+    bool operator==(const GridPosition& other) const;
 
-    void setNamedGridArea(const String& namedGridArea)
-    {
-        m_type = NamedGridAreaPosition;
-        m_namedGridLine = namedGridArea;
-    }
+    bool shouldBeResolvedAgainstOppositePosition() const { return isAuto() || isSpan(); }
 
-    int integerPosition() const
-    {
-        ASSERT(type() == ExplicitPosition);
-        return m_integerPosition;
-    }
+    // Note that grid line 1 is internally represented by the index 0, that's why the max value for
+    // a position is kGridMaxTracks instead of kGridMaxTracks + 1.
+    static int max();
+    static int min();
 
-    String namedGridLine() const
-    {
-        ASSERT(type() == ExplicitPosition || type() == SpanPosition || type() == NamedGridAreaPosition);
-        return m_namedGridLine;
-    }
-
-    int spanPosition() const
-    {
-        ASSERT(type() == SpanPosition);
-        return m_integerPosition;
-    }
-
-    bool operator==(const GridPosition& other) const
-    {
-        return m_type == other.m_type && m_integerPosition == other.m_integerPosition && m_namedGridLine == other.m_namedGridLine;
-    }
-
-    bool shouldBeResolvedAgainstOppositePosition() const
-    {
-        return isAuto() || isSpan();
-    }
+    WEBCORE_EXPORT static void setMaxPositionForTesting(unsigned);
 
 private:
-    void setIntegerPosition(int integerPosition)
-    {
-        m_integerPosition = clampTo(integerPosition, -kGridMaxTracks, kGridMaxTracks);
-    }
+    static std::optional<int> gMaxPositionForTesting;
+
+    void setIntegerPosition(int integerPosition) { m_integerPosition = clampTo(integerPosition, min(), max()); }
 
     GridPositionType m_type;
     int m_integerPosition;
index 614a35a..221ed05 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2016 Igalia S.L.
+ * Copyright (C) 2014-2017 Igalia S.L.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -187,12 +187,12 @@ static void adjustGridPositionsFromStyle(const RenderStyle& gridContainerStyle,
 
 unsigned GridPositionsResolver::explicitGridColumnCount(const RenderStyle& gridContainerStyle, unsigned autoRepeatTracksCount)
 {
-    return std::min<unsigned>(std::max(gridContainerStyle.gridColumns().size() + autoRepeatTracksCount, gridContainerStyle.namedGridAreaColumnCount()), kGridMaxTracks);
+    return std::min<unsigned>(std::max(gridContainerStyle.gridColumns().size() + autoRepeatTracksCount, gridContainerStyle.namedGridAreaColumnCount()), GridPosition::max());
 }
 
 unsigned GridPositionsResolver::explicitGridRowCount(const RenderStyle& gridContainerStyle, unsigned autoRepeatTracksCount)
 {
-    return std::min<unsigned>(std::max(gridContainerStyle.gridRows().size() + autoRepeatTracksCount, gridContainerStyle.namedGridAreaRowCount()), kGridMaxTracks);
+    return std::min<unsigned>(std::max(gridContainerStyle.gridRows().size() + autoRepeatTracksCount, gridContainerStyle.namedGridAreaRowCount()), GridPosition::max());
 }
 
 static unsigned explicitGridSizeForSide(const RenderStyle& gridContainerStyle, GridPositionSide side, unsigned autoRepeatTracksCount)
index a4e6bfd..13e13f5 100644 (file)
@@ -62,6 +62,7 @@
 #include "FrameLoader.h"
 #include "FrameView.h"
 #include "GCObservation.h"
+#include "GridPosition.h"
 #include "HTMLCanvasElement.h"
 #include "HTMLIFrameElement.h"
 #include "HTMLImageElement.h"
@@ -770,6 +771,11 @@ void Internals::setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement
     downcast<BitmapImage>(*image).setClearDecoderAfterAsyncFrameRequestForTesting(value);
 }
 
+void Internals::setGridMaxTracksLimit(unsigned maxTrackLimit)
+{
+    GridPosition::setMaxPositionForTesting(maxTrackLimit);
+}
+
 void Internals::clearPageCache()
 {
     PageCache::singleton().pruneToSizeNow(0, PruningReason::None);
index bc7dde3..3443842 100644 (file)
@@ -118,6 +118,8 @@ public:
     bool isImageAnimating(HTMLImageElement&);
     void setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement&, bool);
 
+    void setGridMaxTracksLimit(unsigned);
+
     void clearPageCache();
     unsigned pageCacheSize() const;
 
index 054ff69..d20bdbc 100644 (file)
@@ -250,6 +250,8 @@ enum EventThrottlingBehavior {
     boolean isImageAnimating(HTMLImageElement element);
     void setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement element, boolean value);
 
+    void setGridMaxTracksLimit(unsigned long maxTracksLimit);
+
     readonly attribute InternalSettings settings;
     readonly attribute unsigned long workerThreadCount;