Skip to content

Commit

Permalink
Cherry-pick 274097.8@webkit-2024.2-embargoed (efd994a148b6). https://…
Browse files Browse the repository at this point in the history
…bugs.webkit.org/show_bug.cgi?id=264639

    ASAN_ILL | WTF::Vector::expandCapacity; WTF::Vector::expandCapacity; WebCore::StyleGradientImage::computeStops
    https://bugs.webkit.org/show_bug.cgi?id=264639

    Reviewed by Antti Koivisto.

    When working with repeating gradients, more care should be put into limiting the
    amount of stops that can be additionally generated. If the original gradient
    range is already too small, the extra stops are not generated. Once the number
    of additional stops is calculated, the generation proceeds only if that number
    is below some reasonable limit. That generation is also improved slightly by
    creating a separate Vector of gradient stops that then simply replaces the
    original one.

    * LayoutTests/fast/css/repeating-conic-gradient-small-range-expected.txt: Added.
    * LayoutTests/fast/css/repeating-conic-gradient-small-range.html: Added.
    * LayoutTests/fast/css/repeating-linear-gradient-small-range-expected.txt: Added.
    * LayoutTests/fast/css/repeating-linear-gradient-small-range.html: Added.
    * LayoutTests/fast/css/repeating-radial-gradient-small-range-expected.txt: Added.
    * LayoutTests/fast/css/repeating-radial-gradient-small-range.html: Added.
    * LayoutTests/platform/glib/TestExpectations:
    * Source/WebCore/rendering/style/StyleGradientImage.cpp:
    (WebCore::StyleGradientImage::computeStops const):

    Canonical link: https://commits.webkit.org/274097.8@webkit-2024.2-embargoed

    Canonical link: https://commits.webkit.org/272448.650@safari-7618-branch

Canonical link: https://commits.webkit.org/274313.221@webkitglib/2.44
  • Loading branch information
zdobersek authored and aperezdc committed May 13, 2024
1 parent 7ef5998 commit 9020818
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
This test passess if it doesn't crash.
Repeating Gradient With Many Stops
Repeating Gradient With Many Stops
Repeating Gradient With Many Stops
19 changes: 19 additions & 0 deletions LayoutTests/fast/css/repeating-conic-gradient-small-range.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script>
if (window.testRunner)
testRunner.dumpAsText();
</script>
<style>
.repeating-gradient-with-suffix-stops {
background: repeating-conic-gradient(from 0deg, red 0%, green 0.0001%);
}
.repeating-gradient-with-prefix-stops {
background: repeating-conic-gradient(from 0deg, red 99.9999%, green 100%);
}
.repeating-gradient-with-prefix-and-suffix-stops {
background: repeating-conic-gradient(from 0deg, red 50%, green 50.0001%);
}
</style>
<div>This test passess if it doesn't crash.</div>
<div class="repeating-gradient-with-suffix-stops">Repeating Gradient With Many Stops</div>
<div class="repeating-gradient-with-prefix-stops">Repeating Gradient With Many Stops</div>
<div class="repeating-gradient-with-prefix-and-suffix-stops">Repeating Gradient With Many Stops</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
This test passess if it doesn't crash.
Repeating Gradient With Many Stops
Repeating Gradient With Many Stops
Repeating Gradient With Many Stops
19 changes: 19 additions & 0 deletions LayoutTests/fast/css/repeating-linear-gradient-small-range.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script>
if (window.testRunner)
testRunner.dumpAsText();
</script>
<style>
.repeating-gradient-with-suffix-stops {
background: repeating-linear-gradient(to right, red 0%, green 0.0001%);
}
.repeating-gradient-with-prefix-stops {
background: repeating-linear-gradient(to right, red 99.9999%, green 100%);
}
.repeating-gradient-with-prefix-and-suffix-stops {
background: repeating-linear-gradient(to right, red 50%, green 50.0001%);
}
</style>
<div>This test passess if it doesn't crash.</div>
<div class="repeating-gradient-with-suffix-stops">Repeating Gradient With Many Stops</div>
<div class="repeating-gradient-with-prefix-stops">Repeating Gradient With Many Stops</div>
<div class="repeating-gradient-with-prefix-and-suffix-stops">Repeating Gradient With Many Stops</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
This test passess if it doesn't crash.
Repeating Gradient With Many Stops
Repeating Gradient With Many Stops
Repeating Gradient With Many Stops
19 changes: 19 additions & 0 deletions LayoutTests/fast/css/repeating-radial-gradient-small-range.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script>
if (window.testRunner)
testRunner.dumpAsText();
</script>
<style>
.repeating-gradient-with-suffix-stops {
background: repeating-radial-gradient(circle at center, red 0%, green 0.0001%);
}
.repeating-gradient-with-prefix-stops {
background: repeating-radial-gradient(circle at center, red 99.9999%, green 100%);
}
.repeating-gradient-with-prefix-and-suffix-stops {
background: repeating-radial-gradient(circle at center, red 50%, green 50.0001%);
}
</style>
<div>This test passess if it doesn't crash.</div>
<div class="repeating-gradient-with-suffix-stops">Repeating Gradient With Many Stops</div>
<div class="repeating-gradient-with-prefix-stops">Repeating Gradient With Many Stops</div>
<div class="repeating-gradient-with-prefix-and-suffix-stops">Repeating Gradient With Many Stops</div>
3 changes: 3 additions & 0 deletions LayoutTests/platform/glib/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -3738,6 +3738,9 @@ webkit.org/b/268238 imported/w3c/web-platform-tests/merchant-validation/complet
webkit.org/b/268238 imported/w3c/web-platform-tests/merchant-validation/constructor.tentative.https.html [ Skip ]
webkit.org/b/268238 imported/w3c/web-platform-tests/merchant-validation/onmerchantvalidation-attribute.https.html [ Skip ]

# This test hits some infinite-loop condition inside Cairo
fast/css/repeating-conic-gradient-small-range.html [ Skip ]

# End: Common failures between GTK and WPE.

#////////////////////////////////////////////////////////////////////////////////////////
Expand Down
101 changes: 76 additions & 25 deletions Source/WebCore/rendering/style/StyleGradientImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -576,49 +576,100 @@ GradientColorStops StyleGradientImage::computeStops(GradientAdapter& gradientAda
stops.first().color = stops.last().color;
stops.shrink(1);
numberOfStops = 1;
} else if (std::abs(gradientRange) < (float(1) / (2 << 15))) {
// If the gradient range is too small, the subsequent replication of stops
// across the complete [0, maxExtent] range can challenging to complete both
// because of potentially-expensive initial traversal across the [0, first-offset]
// and [last-offset, maxExtent] ranges as well as likely exorbitant memory consumption
// needed for all such generated stops. In case of such a gradient range the initial
// Vector of stops remains unchanged, and additional stops for the purpose of the
// repeating nature of the gradient are not computed.
} else {
// Since the gradient range is deemed big enough, the amount of necessary stops is
// calculated for both the [0, first-offset] and the [last-offset, maxExtent] ranges.
float maxExtent = gradientAdapter.maxExtent(maxLengthForRepeat, gradientLength);
CheckedSize numberOfGeneratedStopsBeforeFirst;
CheckedSize numberOfGeneratedStopsAfterLast;

size_t originalNumberOfStops = numberOfStops;
size_t originalFirstStopIndex = 0;

// Work backwards from the first, adding stops until we get one before 0.
float firstOffset = *stops[0].offset;
if (firstOffset > 0) {
float currOffset = firstOffset;
size_t srcStopOrdinal = originalNumberOfStops - 1;
if (*stops.first().offset > 0) {
float currOffset = *stops.first().offset;
size_t srcStopOrdinal = numberOfStops - 1;

while (true) {
auto newStop = stops[originalFirstStopIndex + srcStopOrdinal];
newStop.offset = currOffset;
stops.insert(0, newStop);
++originalFirstStopIndex;
++numberOfGeneratedStopsBeforeFirst;
if (currOffset < 0)
break;

if (srcStopOrdinal)
currOffset -= *stops[originalFirstStopIndex + srcStopOrdinal].offset - *stops[originalFirstStopIndex + srcStopOrdinal - 1].offset;
srcStopOrdinal = (srcStopOrdinal + originalNumberOfStops - 1) % originalNumberOfStops;
currOffset -= *stops[srcStopOrdinal].offset - *stops[srcStopOrdinal - 1].offset;
srcStopOrdinal = (srcStopOrdinal + numberOfStops - 1) % numberOfStops;
}
}

// Work forwards from the end, adding stops until we get one after 1.
float lastOffset = *stops[stops.size() - 1].offset;
if (lastOffset < maxExtent) {
float currOffset = lastOffset;
if (*stops.last().offset < maxExtent) {
float currOffset = *stops.last().offset;
size_t srcStopOrdinal = 0;

while (true) {
size_t srcStopIndex = originalFirstStopIndex + srcStopOrdinal;
auto newStop = stops[srcStopIndex];
newStop.offset = currOffset;
stops.append(newStop);
++numberOfGeneratedStopsAfterLast;
if (currOffset > maxExtent)
break;
if (srcStopOrdinal < originalNumberOfStops - 1)
currOffset += *stops[srcStopIndex + 1].offset - *stops[srcStopIndex].offset;
srcStopOrdinal = (srcStopOrdinal + 1) % originalNumberOfStops;

if (srcStopOrdinal < numberOfStops - 1)
currOffset += *stops[srcStopOrdinal + 1].offset - *stops[srcStopOrdinal].offset;
srcStopOrdinal = (srcStopOrdinal + 1) % numberOfStops;
}
}

// With the number of stops necessary for the repeating gradient now known, we can impose
// some reasonable limit to prevent generation of memory-expensive amounts of gradient stops.
CheckedSize checkedNumberOfGeneratedStops = CheckedSize(numberOfStops) + numberOfGeneratedStopsBeforeFirst + numberOfGeneratedStopsAfterLast;
if (checkedNumberOfGeneratedStops.hasOverflowed() || checkedNumberOfGeneratedStops.value() > (2 << 15)) {
// More than 65536 gradient stops are expected. Let's fall back to the initially-provided
// Vector of stops, effectively meaning the repetition of stops is not applied.
} else {
// An affordable amount of gradient stops is determined. A separate Vector object is constructed
// accordingly, first generating the repeated stops in the [0, first-offset] range, then adding
// the original stops, and finally generating the repeated stops in the [last-offset, maxExtent]
// range. The resulting Vector is then moved in to replace the original stops.
Vector<ResolvedGradientStop> generatedStops;
generatedStops.reserveInitialCapacity(checkedNumberOfGeneratedStops.value());

if (numberOfGeneratedStopsBeforeFirst > 0) {
float currOffset = *stops.first().offset;
size_t srcStopOrdinal = numberOfStops - 1;

for (size_t i = 0; i < numberOfGeneratedStopsBeforeFirst; ++i) {
auto newStop = stops[srcStopOrdinal];
newStop.offset = currOffset;
generatedStops.append(newStop);

if (srcStopOrdinal)
currOffset -= *stops[srcStopOrdinal].offset - *stops[srcStopOrdinal - 1].offset;
srcStopOrdinal = (srcStopOrdinal + numberOfStops - 1) % numberOfStops;
}

generatedStops.reverse();
}

generatedStops.appendVector(stops);

if (numberOfGeneratedStopsAfterLast > 0) {
float currOffset = *stops.last().offset;
size_t srcStopOrdinal = 0;

for (size_t i = 0; i < numberOfGeneratedStopsAfterLast; ++i) {
auto newStop = stops[srcStopOrdinal];
newStop.offset = currOffset;
generatedStops.append(newStop);

if (srcStopOrdinal < numberOfStops - 1)
currOffset += *stops[srcStopOrdinal + 1].offset - *stops[srcStopOrdinal].offset;
srcStopOrdinal = (srcStopOrdinal + 1) % numberOfStops;
}
}

stops = WTFMove(generatedStops);
}
}
}
Expand Down

0 comments on commit 9020818

Please sign in to comment.