Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LibWeb/HTML: Fix crash creating canvas pattern without context #3223

Merged
merged 1 commit into from
Jan 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions Libraries/LibWeb/HTML/CanvasPattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,17 @@ void CanvasPatternPaintStyle::paint(Gfx::IntRect physical_bounding_box, PaintFun

// 6. The resulting bitmap is what is to be rendered, with the same origin and same scale.

auto const bitmap_width = m_immutable_bitmap->width();
auto const bitmap_height = m_immutable_bitmap->height();
// FIXME: This doesn't handle a 'none' canvas context mode.
auto bitmap = m_image.visit(
[](GC::Root<HTMLImageElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return source->immutable_bitmap(); },
[](GC::Root<SVG::SVGImageElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return source->current_image_bitmap(); },
[](GC::Root<HTMLCanvasElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create_snapshot_from_painting_surface(*source->surface()); },
[](GC::Root<HTMLVideoElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create(*source->bitmap()); },
[](GC::Root<ImageBitmap> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create(*source->bitmap()); });
VERIFY(bitmap);

auto const bitmap_width = bitmap->width();
auto const bitmap_height = bitmap->height();

paint([=, this](auto point) {
point.translate_by(physical_bounding_box.location());
Expand Down Expand Up @@ -78,8 +87,8 @@ void CanvasPatternPaintStyle::paint(Gfx::IntRect physical_bounding_box, PaintFun
VERIFY_NOT_REACHED();
}
}();
if (m_immutable_bitmap->rect().contains(point))
return m_immutable_bitmap->get_pixel(point.x(), point.y());
if (bitmap->rect().contains(point))
return bitmap->get_pixel(point.x(), point.y());
return Gfx::Color();
});
}
Expand Down Expand Up @@ -127,16 +136,8 @@ WebIDL::ExceptionOr<GC::Ptr<CanvasPattern>> CanvasPattern::create(JS::Realm& rea
if (!repetition_value.has_value())
return WebIDL::SyntaxError::create(realm, "Repetition value is not valid"_string);

// Note: Bitmap won't be null here, as if it were it would have "bad" usability.
auto bitmap = image.visit(
[](GC::Root<HTMLImageElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return source->immutable_bitmap(); },
[](GC::Root<SVG::SVGImageElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return source->current_image_bitmap(); },
[](GC::Root<HTMLCanvasElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create_snapshot_from_painting_surface(*source->surface()); },
[](GC::Root<HTMLVideoElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create(*source->bitmap()); },
[](GC::Root<ImageBitmap> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create(*source->bitmap()); });

// 6. Let pattern be a new CanvasPattern object with the image image and the repetition behavior given by repetition.
auto pattern = TRY_OR_THROW_OOM(realm.vm(), CanvasPatternPaintStyle::create(*bitmap, *repetition_value));
auto pattern = TRY_OR_THROW_OOM(realm.vm(), CanvasPatternPaintStyle::create(image, *repetition_value));

// FIXME: 7. If image is not origin-clean, then mark pattern as not origin-clean.

Expand Down
11 changes: 6 additions & 5 deletions Libraries/LibWeb/HTML/CanvasPattern.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
* Copyright (c) 2025, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
Expand All @@ -21,21 +22,21 @@ class CanvasPatternPaintStyle final : public Gfx::PaintStyle {
NoRepeat
};

static ErrorOr<NonnullRefPtr<CanvasPatternPaintStyle>> create(Gfx::ImmutableBitmap const& bitmap, Repetition repetition)
static ErrorOr<NonnullRefPtr<CanvasPatternPaintStyle>> create(CanvasImageSource image, Repetition repetition)
{
return adopt_nonnull_ref_or_enomem(new (nothrow) CanvasPatternPaintStyle(bitmap, repetition));
return adopt_nonnull_ref_or_enomem(new (nothrow) CanvasPatternPaintStyle(move(image), repetition));
}

virtual void paint(Gfx::IntRect physical_bounding_box, PaintFunction paint) const override;

private:
CanvasPatternPaintStyle(Gfx::ImmutableBitmap const& immutable_bitmap, Repetition repetition)
: m_immutable_bitmap(immutable_bitmap)
CanvasPatternPaintStyle(CanvasImageSource image, Repetition repetition)
: m_image(move(image))
, m_repetition(repetition)
{
}

NonnullRefPtr<Gfx::ImmutableBitmap> m_immutable_bitmap;
CanvasImageSource m_image;
Repetition m_repetition { Repetition::Repeat };
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Harness status: OK

Found 1 tests

1 Fail
Fail Canvas test: 2d.pattern.basic.nocontext
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Harness status: OK

Found 1 tests

1 Pass
Pass Canvas test: 2d.pattern.repeat.null
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Harness status: OK

Found 1 tests

1 Fail
Fail Canvas test: 2d.pattern.transform.identity
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Harness status: OK

Found 1 tests

1 Fail
Fail Canvas test: 2d.pattern.transform.infinity
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Harness status: OK

Found 1 tests

1 Pass
Pass Canvas test: 2d.pattern.transform.invalid
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.pattern.basic.nocontext</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">

<h1>2d.pattern.basic.nocontext</h1>
<p class="desc"></p>


<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
<ul id="d"></ul>
<script>
var t = async_test("");
_addTest(function(canvas, ctx) {

var canvas2 = document.createElement('canvas');
canvas2.width = 100;
canvas2.height = 50;
var pattern = ctx.createPattern(canvas2, 'no-repeat');

ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#f00';
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);

_assertPixel(canvas, 1,1, 0,255,0,255);
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);

});
</script>

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.pattern.repeat.null</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">

<h1>2d.pattern.repeat.null</h1>
<p class="desc"></p>


<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>

<ul id="d"></ul>
<script>
var t = async_test("");
_addTest(function(canvas, ctx) {

_assert(ctx.createPattern(canvas, null) != null, "ctx.createPattern(canvas, null) != null");

});
</script>

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.pattern.transform.identity</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">

<h1>2d.pattern.transform.identity</h1>
<p class="desc"></p>


<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
<ul id="d"></ul>
<script>
var t = async_test("");
_addTest(function(canvas, ctx) {

var canvas2 = document.createElement('canvas');
canvas2.width = 100;
canvas2.height = 50;
var pattern = ctx.createPattern(canvas2, 'no-repeat');
pattern.setTransform(new DOMMatrix());

ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#f00';
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);

_assertPixel(canvas, 1,1, 0,255,0,255);
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);

});
</script>

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.pattern.transform.infinity</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">

<h1>2d.pattern.transform.infinity</h1>
<p class="desc"></p>


<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
<ul id="d"></ul>
<script>
var t = async_test("");
_addTest(function(canvas, ctx) {

var canvas2 = document.createElement('canvas');
canvas2.width = 100;
canvas2.height = 50;
var pattern = ctx.createPattern(canvas2, 'no-repeat');
pattern.setTransform({a: Infinity});

ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#f00';
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);

_assertPixel(canvas, 1,1, 0,255,0,255);
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);

});
</script>

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.pattern.transform.invalid</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">

<h1>2d.pattern.transform.invalid</h1>
<p class="desc"></p>


<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>

<ul id="d"></ul>
<script>
var t = async_test("");
_addTest(function(canvas, ctx) {

var canvas2 = document.createElement('canvas');
canvas2.width = 100;
canvas2.height = 50;
var pattern = ctx.createPattern(canvas2, 'no-repeat');
assert_throws_js(TypeError, function() { pattern.setTransform({a: 1, m11: 2}); });

});
</script>

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading