LibWeb/HTML: Avoid crash for extreme bitmap sizes in OffscreenCanvas

This commit is contained in:
Shannon Booth 2025-09-20 18:36:10 +02:00 committed by Sam Atkins
commit e3b5507113
Notes: github-actions[bot] 2025-09-22 11:38:58 +00:00
4 changed files with 51 additions and 5 deletions

View file

@ -134,12 +134,18 @@ void OffscreenCanvas::reset_context_to_default_state()
});
}
void OffscreenCanvas::set_new_bitmap_size(Gfx::IntSize new_size)
WebIDL::ExceptionOr<void> OffscreenCanvas::set_new_bitmap_size(Gfx::IntSize new_size)
{
if (new_size.width() == 0 || new_size.height() == 0)
m_bitmap = nullptr;
else {
m_bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA8888, Gfx::IntSize { new_size.width(), new_size.height() }));
// FIXME: Other browsers appear to not throw for unreasonable sizes being set. We could consider deferring allocation of the bitmap until it is used,
// but for now, lets just allocate it here and throw if it fails instead of crashing.
auto bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA8888, Gfx::IntSize { new_size.width(), new_size.height() });
if (bitmap_or_error.is_error()) {
return WebIDL::InvalidStateError::create(realm(), Utf16String::formatted("Error in allocating bitmap: {}", bitmap_or_error.error()));
}
m_bitmap = bitmap_or_error.release_value();
}
m_context.visit(
@ -155,7 +161,9 @@ void OffscreenCanvas::set_new_bitmap_size(Gfx::IntSize new_size)
[](Empty) {
// Do nothing.
});
return {};
}
RefPtr<Gfx::Bitmap> OffscreenCanvas::bitmap() const
{
return m_bitmap;
@ -166,7 +174,7 @@ WebIDL::ExceptionOr<void> OffscreenCanvas::set_width(WebIDL::UnsignedLong value)
Gfx::IntSize current_size = bitmap_size_for_canvas();
current_size.set_width(value);
set_new_bitmap_size(current_size);
TRY(set_new_bitmap_size(current_size));
reset_context_to_default_state();
return {};
}
@ -175,7 +183,7 @@ WebIDL::ExceptionOr<void> OffscreenCanvas::set_height(WebIDL::UnsignedLong value
Gfx::IntSize current_size = bitmap_size_for_canvas();
current_size.set_height(value);
set_new_bitmap_size(current_size);
TRY(set_new_bitmap_size(current_size));
reset_context_to_default_state();
return {};
}

View file

@ -80,7 +80,7 @@ private:
JS::ThrowCompletionOr<HasOrCreatedContext> create_2d_context(JS::Value options);
void reset_context_to_default_state();
void set_new_bitmap_size(Gfx::IntSize new_size);
WebIDL::ExceptionOr<void> set_new_bitmap_size(Gfx::IntSize new_size);
Variant<GC::Ref<HTML::OffscreenCanvasRenderingContext2D>, GC::Ref<WebGL::WebGLRenderingContext>, GC::Ref<WebGL::WebGL2RenderingContext>, Empty> m_context;

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Fail
Fail OffscreenCanvas test: 2d.canvas.host.size.large

View file

@ -0,0 +1,32 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>OffscreenCanvas test: 2d.canvas.host.size.large</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<h1>2d.canvas.host.size.large</h1>
<p class="desc"></p>
<p class="notes">Not sure how reasonable this is, but the spec doesn't say there's an upper limit on the size.
<script>
var t = async_test("");
var t_pass = t.done.bind(t);
var t_fail = t.step_func(function(reason) {
throw reason;
});
t.step(function() {
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
var n = 2147483647; // 2^31 - 1, which should be supported by any sensible definition of "long"
canvas.width = n;
canvas.height = n;
_assertSame(canvas.width, n, "canvas.width", "n");
_assertSame(canvas.height, n, "canvas.height", "n");
t.done();
});
</script>