LibWeb: Serialize empty ImageBitmap data as null

This commit is contained in:
Tim Ledbetter 2025-07-20 18:29:06 +01:00 committed by Tim Flynn
commit 92f85d180e
Notes: github-actions[bot] 2025-07-21 23:21:07 +00:00
4 changed files with 115 additions and 10 deletions

View file

@ -20,19 +20,26 @@ GC_DEFINE_ALLOCATOR(ImageBitmap);
return Gfx::Bitmap::create_wrapper(format, alpha_type, Gfx::IntSize(width, height), pitch, data.data());
}
static void serialize_bitmap(HTML::TransferDataEncoder& encoder, Gfx::Bitmap& bitmap)
static void serialize_bitmap(HTML::TransferDataEncoder& encoder, RefPtr<Gfx::Bitmap> const& bitmap)
{
encoder.encode(bitmap.width());
encoder.encode(bitmap.height());
encoder.encode(bitmap.pitch());
encoder.encode(bitmap.format());
encoder.encode(bitmap.alpha_type());
encoder.encode(ReadonlyBytes { bitmap.scanline_u8(0), bitmap.data_size() });
if (!bitmap) {
encoder.encode(0);
return;
}
encoder.encode(bitmap->width());
encoder.encode(bitmap->height());
encoder.encode(bitmap->pitch());
encoder.encode(bitmap->format());
encoder.encode(bitmap->alpha_type());
encoder.encode(ReadonlyBytes { bitmap->scanline_u8(0), bitmap->data_size() });
}
[[nodiscard]] static WebIDL::ExceptionOr<NonnullRefPtr<Gfx::Bitmap>> deserialize_bitmap(JS::Realm& realm, HTML::TransferDataDecoder& decoder)
[[nodiscard]] static WebIDL::ExceptionOr<RefPtr<Gfx::Bitmap>> deserialize_bitmap(JS::Realm& realm, HTML::TransferDataDecoder& decoder)
{
auto const width = decoder.decode<int>();
if (width == 0)
return nullptr;
auto const height = decoder.decode<int>();
auto const pitch = decoder.decode<size_t>();
auto const format = decoder.decode<Gfx::BitmapFormat>();
@ -68,7 +75,7 @@ WebIDL::ExceptionOr<void> ImageBitmap::serialization_steps(HTML::TransferDataEnc
// FIXME: 1. If value's origin-clean flag is not set, then throw a "DataCloneError" DOMException.
// 2. Set serialized.[[BitmapData]] to a copy of value's bitmap data.
serialize_bitmap(serialized, *m_bitmap);
serialize_bitmap(serialized, m_bitmap);
return {};
}
@ -88,7 +95,7 @@ WebIDL::ExceptionOr<void> ImageBitmap::transfer_steps(HTML::TransferDataEncoder&
// FIXME: 1. If value's origin-clean flag is not set, then throw a "DataCloneError" DOMException.
// 2. Set dataHolder.[[BitmapData]] to value's bitmap data.
serialize_bitmap(data_holder, *m_bitmap);
serialize_bitmap(data_holder, m_bitmap);
// 3. Unset value's bitmap data.
m_bitmap = nullptr;

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass canvas-ImageBitmap-close

View file

@ -0,0 +1,89 @@
<!DOCTYPE html>
<body>
<p>Tests that the close method of ImageBitmap does dispose the image data.</p>
<script src="../../../../../resources/testharness.js"></script>
<script src="../../../../../resources/testharnessreport.js"></script>
<script>
promise_test(function(t) {
var worker = new Worker('worker-onmessage-noop.js');
var imgHeight = 10;
var imgWidth = 10;
var imageData = new ImageData(10, 10);
var bitmap;
var ctx;
return createImageBitmap(imageData).then(imageBitmap => {
bitmap = imageBitmap;
assert_equals(bitmap.width, imgWidth, "bitmap.width = 10");
assert_equals(bitmap.height, imgWidth, "bitmap.height = 10");
// Apply structured clone to the bitmap, nothing should be changed
worker.postMessage({data: bitmap});
assert_equals(bitmap.width, imgWidth, "bitmap.width = 10");
assert_equals(bitmap.height, imgWidth, "bitmap.height = 10");
// After calling close, the image data associated with the bitmap should no longer exist
bitmap.close();
assert_equals(bitmap.width, 0, "bitmap.width = 0");
assert_equals(bitmap.height, 0, "bitmap.height = 0");
var canvas = document.createElement("canvas");
canvas.width = imgWidth;
canvas.height = imgHeight;
ctx = canvas.getContext("2d");
assert_throws_dom("InvalidStateError", function() { ctx.drawImage(bitmap, 0, 0); });
// Try to apply structured clone to an already closed bitmap
try {
worker.postMessage({data: bitmap});
throw new Error("Apply clone to an closed bitmap should be rejected");
}
catch(ex) {
// Apply structured clone to an already closed bitmap is rejected as expected.
}
// Try to apply transferring to an already closed bitmap
try {
worker.postMessage({data: bitmap}, [bitmap]);
throw new Error("Apply transferring to an closed bitmap should be rejected");
} catch(ex) {
// Apply structured clone to an already closed bitmap is rejected as expected.
}
// Calling createImageBitmap from a closed bitmap should be rejected
return createImageBitmap(bitmap).then(function() {
throw new Error("createImageBitmap from a closed bitmap should be rejected");
}, ex => {
// Calling createImageBitmap from a closed ImageBitmap is rejected as expected.
});
}).then(() => {
// Call close to a already closed bitmap should be noop.
bitmap.close();
assert_equals(bitmap.width, 0, "bitmap.height = 0");
assert_equals(bitmap.height, 0, "bitmap.height = 0");
return createImageBitmap(imageData).then(imageBitmap => {
bitmap = imageBitmap;
assert_equals(bitmap.width, imgWidth, "bitmap.width = 10");
assert_equals(bitmap.height, imgWidth, "bitmap.height = 10");
// Transfer the bitmap to a worker
worker.postMessage({data: bitmap}, [bitmap]);
// After transferring, the bitmap is neutered.
assert_equals(bitmap.width, 0, "bitmap.height = 0");
assert_equals(bitmap.height, 0, "bitmap.height = 0");
// Calling close to a neutered bitmap should be noop.
bitmap.close();
assert_equals(bitmap.width, 0, "bitmap.height = 0");
assert_equals(bitmap.height, 0, "bitmap.height = 0");
});
}).catch(function(ex) {
throw new Error("No exception should be thrown.");
})
});
</script>

View file

@ -0,0 +1,3 @@
self.onmessage = function(e) {
};