mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 03:25:13 +00:00
LibWeb/HTML: Fix crash creating canvas pattern without context
This isn't a full fix, as the paint function does not handle this either. But instead of getting the bitmap from the image source immediately, follow the spec a bit more closely by creating the CanvasPatern object with the ImageSource directly. Fixes a crash for the 5 included WPT tests.
This commit is contained in:
parent
dfdcfc8e88
commit
f388d3c88c
Notes:
github-actions[bot]
2025-01-12 18:41:35 +00:00
Author: https://github.com/shannonbooth Commit: https://github.com/LadybirdBrowser/ladybird/commit/f388d3c88c9 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3223 Reviewed-by: https://github.com/tcl3 ✅
13 changed files with 233 additions and 18 deletions
|
@ -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());
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
@ -21,21 +22,21 @@ public:
|
|||
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 };
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 1 tests
|
||||
|
||||
1 Fail
|
||||
Fail Canvas test: 2d.pattern.basic.nocontext
|
|
@ -0,0 +1,6 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 1 tests
|
||||
|
||||
1 Pass
|
||||
Pass Canvas test: 2d.pattern.repeat.null
|
|
@ -0,0 +1,6 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 1 tests
|
||||
|
||||
1 Fail
|
||||
Fail Canvas test: 2d.pattern.transform.identity
|
|
@ -0,0 +1,6 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 1 tests
|
||||
|
||||
1 Fail
|
||||
Fail Canvas test: 2d.pattern.transform.infinity
|
|
@ -0,0 +1,6 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 1 tests
|
||||
|
||||
1 Pass
|
||||
Pass Canvas test: 2d.pattern.transform.invalid
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 86 B |
Loading…
Add table
Reference in a new issue