mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-15 23:09:05 +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: f388d3c88c
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.
|
// 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();
|
// FIXME: This doesn't handle a 'none' canvas context mode.
|
||||||
auto const bitmap_height = m_immutable_bitmap->height();
|
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) {
|
paint([=, this](auto point) {
|
||||||
point.translate_by(physical_bounding_box.location());
|
point.translate_by(physical_bounding_box.location());
|
||||||
|
@ -78,8 +87,8 @@ void CanvasPatternPaintStyle::paint(Gfx::IntRect physical_bounding_box, PaintFun
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
if (m_immutable_bitmap->rect().contains(point))
|
if (bitmap->rect().contains(point))
|
||||||
return m_immutable_bitmap->get_pixel(point.x(), point.y());
|
return bitmap->get_pixel(point.x(), point.y());
|
||||||
return Gfx::Color();
|
return Gfx::Color();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -127,16 +136,8 @@ WebIDL::ExceptionOr<GC::Ptr<CanvasPattern>> CanvasPattern::create(JS::Realm& rea
|
||||||
if (!repetition_value.has_value())
|
if (!repetition_value.has_value())
|
||||||
return WebIDL::SyntaxError::create(realm, "Repetition value is not valid"_string);
|
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.
|
// 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.
|
// 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) 2023, MacDue <macdue@dueutil.tech>
|
||||||
|
* Copyright (c) 2025, Shannon Booth <shannon@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -21,21 +22,21 @@ public:
|
||||||
NoRepeat
|
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;
|
virtual void paint(Gfx::IntRect physical_bounding_box, PaintFunction paint) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CanvasPatternPaintStyle(Gfx::ImmutableBitmap const& immutable_bitmap, Repetition repetition)
|
CanvasPatternPaintStyle(CanvasImageSource image, Repetition repetition)
|
||||||
: m_immutable_bitmap(immutable_bitmap)
|
: m_image(move(image))
|
||||||
, m_repetition(repetition)
|
, m_repetition(repetition)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullRefPtr<Gfx::ImmutableBitmap> m_immutable_bitmap;
|
CanvasImageSource m_image;
|
||||||
Repetition m_repetition { Repetition::Repeat };
|
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
Add a link
Reference in a new issue