diff --git a/Libraries/LibWeb/HTML/ImageData.cpp b/Libraries/LibWeb/HTML/ImageData.cpp
index 39c07c8493d..01631102708 100644
--- a/Libraries/LibWeb/HTML/ImageData.cpp
+++ b/Libraries/LibWeb/HTML/ImageData.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2020-2022, Andreas Kling
- * Copyright (c) 2024, Kenneth Myhra
+ * Copyright (c) 2024-2025, Kenneth Myhra
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -18,6 +19,16 @@ namespace Web::HTML {
GC_DEFINE_ALLOCATOR(ImageData);
+[[nodiscard]] static auto create_bitmap_backed_by_uint8_clamped_array(u32 const width, u32 const height, JS::Uint8ClampedArray& data)
+{
+ return Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA8888, Gfx::AlphaType::Unpremultiplied, Gfx::IntSize(width, height), width * sizeof(u32), data.data().data());
+}
+
+GC::Ref ImageData::create(JS::Realm& realm)
+{
+ return realm.create(realm);
+}
+
// https://html.spec.whatwg.org/multipage/canvas.html#dom-imagedata
WebIDL::ExceptionOr> ImageData::create(JS::Realm& realm, u32 sw, u32 sh, Optional const& settings)
{
@@ -102,7 +113,7 @@ WebIDL::ExceptionOr> ImageData::initialize(JS::Realm& realm,
}());
// AD-HOC: Create the bitmap backed by the Uint8ClampedArray.
- auto bitmap = TRY_OR_THROW_OOM(realm.vm(), Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA8888, Gfx::AlphaType::Unpremultiplied, Gfx::IntSize(pixels_per_row, rows), pixels_per_row * sizeof(u32), data->data().data()));
+ auto bitmap = TRY_OR_THROW_OOM(realm.vm(), create_bitmap_backed_by_uint8_clamped_array(pixels_per_row, rows, *data));
// 4. Initialize the width attribute of imageData to pixelsPerRow.
// 5. Initialize the height attribute of imageData to rows.
@@ -121,6 +132,11 @@ WebIDL::ExceptionOr> ImageData::initialize(JS::Realm& realm,
return realm.create(realm, move(bitmap), data, color_space);
}
+ImageData::ImageData(JS::Realm& realm)
+ : PlatformObject(realm)
+{
+}
+
ImageData::ImageData(JS::Realm& realm, NonnullRefPtr bitmap, GC::Ref data, Bindings::PredefinedColorSpace color_space)
: PlatformObject(realm)
, m_bitmap(move(bitmap))
@@ -163,4 +179,57 @@ const JS::Uint8ClampedArray* ImageData::data() const
return m_data;
}
+// https://html.spec.whatwg.org/multipage/canvas.html#pixel-manipulation:serialization-steps
+WebIDL::ExceptionOr ImageData::serialization_steps(SerializationRecord& serialized, bool for_storage, SerializationMemory& memory)
+{
+ auto& vm = this->vm();
+
+ // 1. Set serialized.[[Data]] to the sub-serialization of the value of value's data attribute.
+ auto serialized_data = TRY(structured_serialize_internal(vm, m_data, for_storage, memory));
+ serialized.extend(move(serialized_data));
+
+ // 2. Set serialized.[[Width]] to the value of value's width attribute.
+ serialize_primitive_type(serialized, m_bitmap->width());
+
+ // 3. Set serialized.[[Height]] to the value of value's height attribute.
+ serialize_primitive_type(serialized, m_bitmap->height());
+
+ // 4. Set serialized.[[ColorSpace]] to the value of value's colorSpace attribute.
+ serialize_enum(serialized, m_color_space);
+
+ // FIXME:: 5. Set serialized.[[PixelFormat]] to the value of value's pixelFormat attribute.
+
+ return {};
+}
+
+// https://html.spec.whatwg.org/multipage/canvas.html#pixel-manipulation:deserialization-steps
+WebIDL::ExceptionOr ImageData::deserialization_steps(ReadonlySpan const& serialized, size_t& position, DeserializationMemory& memory)
+{
+ auto& vm = this->vm();
+ auto& realm = this->realm();
+
+ // 1. Initialize value's data attribute to the sub-deserialization of serialized.[[Data]].
+ auto [value, position_after_deserialization] = TRY(structured_deserialize_internal(vm, serialized, realm, memory, position));
+ if (value.has_value() && value.value().is_object() && is(value.value().as_object())) {
+ m_data = as(value.release_value().as_object());
+ }
+ position = position_after_deserialization;
+
+ // 2. Initialize value's width attribute to serialized.[[Width]].
+ auto const width = deserialize_primitive_type(serialized, position);
+
+ // 3. Initialize value's height attribute to serialized.[[Height]].
+ auto const height = deserialize_primitive_type(serialized, position);
+
+ // 4. Initialize value's colorSpace attribute to serialized.[[ColorSpace]].
+ m_color_space = deserialize_primitive_type(serialized, position);
+
+ // FIXME: 5. Initialize value's pixelFormat attribute to serialized.[[PixelFormat]].
+
+ // AD-HOC: Create the bitmap backed by the Uint8ClampedArray.
+ m_bitmap = TRY_OR_THROW_OOM(vm, create_bitmap_backed_by_uint8_clamped_array(width, height, *m_data));
+
+ return {};
+}
+
}
diff --git a/Libraries/LibWeb/HTML/ImageData.h b/Libraries/LibWeb/HTML/ImageData.h
index 3fa6036e5bb..c373ec1ab4a 100644
--- a/Libraries/LibWeb/HTML/ImageData.h
+++ b/Libraries/LibWeb/HTML/ImageData.h
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2020-2022, Andreas Kling
- * Copyright (c) 2024, Kenneth Myhra
+ * Copyright (c) 2024-2025, Kenneth Myhra
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
namespace Web::HTML {
@@ -18,11 +19,14 @@ struct ImageDataSettings {
Bindings::PredefinedColorSpace color_space;
};
-class ImageData final : public Bindings::PlatformObject {
+class ImageData final
+ : public Bindings::PlatformObject
+ , public Bindings::Serializable {
WEB_PLATFORM_OBJECT(ImageData, Bindings::PlatformObject);
GC_DECLARE_ALLOCATOR(ImageData);
public:
+ [[nodiscard]] static GC::Ref create(JS::Realm&);
[[nodiscard]] static WebIDL::ExceptionOr> create(JS::Realm&, u32 sw, u32 sh, Optional const& settings = {});
[[nodiscard]] static WebIDL::ExceptionOr> create(JS::Realm&, GC::Root const& data, u32 sw, Optional sh = {}, Optional const& settings = {});
@@ -34,25 +38,30 @@ public:
WebIDL::UnsignedLong width() const;
WebIDL::UnsignedLong height() const;
- Gfx::Bitmap& bitmap() { return m_bitmap; }
- Gfx::Bitmap const& bitmap() const { return m_bitmap; }
+ Gfx::Bitmap& bitmap() { return *m_bitmap; }
+ Gfx::Bitmap const& bitmap() const { return *m_bitmap; }
JS::Uint8ClampedArray* data();
const JS::Uint8ClampedArray* data() const;
Bindings::PredefinedColorSpace color_space() const { return m_color_space; }
+ virtual StringView interface_name() const override { return "ImageData"sv; }
+ virtual WebIDL::ExceptionOr serialization_steps(SerializationRecord& serialized, bool for_storage, SerializationMemory&) override;
+ virtual WebIDL::ExceptionOr deserialization_steps(ReadonlySpan const& serialized, size_t& position, DeserializationMemory&) override;
+
private:
[[nodiscard]] static WebIDL::ExceptionOr> initialize(JS::Realm&, u32 rows, u32 pixels_per_row, Optional const&, GC::Ptr = {}, Optional = {});
+ explicit ImageData(JS::Realm&);
ImageData(JS::Realm&, NonnullRefPtr, GC::Ref, Bindings::PredefinedColorSpace);
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override;
- NonnullRefPtr m_bitmap;
- Bindings::PredefinedColorSpace m_color_space;
- GC::Ref m_data;
+ RefPtr m_bitmap;
+ Bindings::PredefinedColorSpace m_color_space { Bindings::PredefinedColorSpace::Srgb };
+ GC::Ptr m_data;
};
}
diff --git a/Libraries/LibWeb/HTML/StructuredSerialize.cpp b/Libraries/LibWeb/HTML/StructuredSerialize.cpp
index 6f1566f63db..498a2a6c9c3 100644
--- a/Libraries/LibWeb/HTML/StructuredSerialize.cpp
+++ b/Libraries/LibWeb/HTML/StructuredSerialize.cpp
@@ -46,6 +46,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -1069,6 +1070,8 @@ private:
return Crypto::CryptoKey::create(realm);
if (interface_name == "DOMQuad"sv)
return Geometry::DOMQuad::create(realm);
+ if (interface_name == "ImageData"sv)
+ return ImageData::create(realm);
VERIFY_NOT_REACHED();
}
diff --git a/Tests/LibWeb/Text/expected/HTML/StructuredClone-serializable-objects.txt b/Tests/LibWeb/Text/expected/HTML/StructuredClone-serializable-objects.txt
index e0fa72287c3..062d37e576e 100644
--- a/Tests/LibWeb/Text/expected/HTML/StructuredClone-serializable-objects.txt
+++ b/Tests/LibWeb/Text/expected/HTML/StructuredClone-serializable-objects.txt
@@ -29,3 +29,10 @@ CryptoKey.algorithm: {"name":"PBKDF2"}
CryptoKey.usages: ["deriveKey","deriveBits"]
instanceOf DOMException: true
DOMException: Index out of bounds - IndexSizeError
+instanceOf ImageData: true
+ImageData.width: 2
+ImageData.height: 2
+255, 0, 0, 255
+0, 255, 0, 255
+0, 0, 255, 255
+255, 255, 0, 255
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/infrastructure/safe-passing-of-structured-data/window-postmessage.window.txt b/Tests/LibWeb/Text/expected/wpt-import/html/infrastructure/safe-passing-of-structured-data/window-postmessage.window.txt
index cf819fd63e0..2363c8a9128 100644
--- a/Tests/LibWeb/Text/expected/wpt-import/html/infrastructure/safe-passing-of-structured-data/window-postmessage.window.txt
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/infrastructure/safe-passing-of-structured-data/window-postmessage.window.txt
@@ -2,8 +2,8 @@ Harness status: OK
Found 150 tests
-134 Pass
-15 Fail
+140 Pass
+9 Fail
1 Optional Feature Unsupported
Pass primitive undefined
Pass primitive null
@@ -109,12 +109,12 @@ Pass File basic
Pass FileList empty
Pass Array FileList object, FileList empty
Pass Object FileList object, FileList empty
-Fail ImageData 1x1 transparent black
-Fail ImageData 1x1 non-transparent non-black
-Fail Array ImageData object, ImageData 1x1 transparent black
-Fail Array ImageData object, ImageData 1x1 non-transparent non-black
-Fail Object ImageData object, ImageData 1x1 transparent black
-Fail Object ImageData object, ImageData 1x1 non-transparent non-black
+Pass ImageData 1x1 transparent black
+Pass ImageData 1x1 non-transparent non-black
+Pass Array ImageData object, ImageData 1x1 transparent black
+Pass Array ImageData object, ImageData 1x1 non-transparent non-black
+Pass Object ImageData object, ImageData 1x1 transparent black
+Pass Object ImageData object, ImageData 1x1 non-transparent non-black
Pass Array sparse
Pass Array with non-index property
Pass Object with index property and length
diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/structured-clone/structured-clone.any.txt b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/structured-clone/structured-clone.any.txt
index cf819fd63e0..2363c8a9128 100644
--- a/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/structured-clone/structured-clone.any.txt
+++ b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/structured-clone/structured-clone.any.txt
@@ -2,8 +2,8 @@ Harness status: OK
Found 150 tests
-134 Pass
-15 Fail
+140 Pass
+9 Fail
1 Optional Feature Unsupported
Pass primitive undefined
Pass primitive null
@@ -109,12 +109,12 @@ Pass File basic
Pass FileList empty
Pass Array FileList object, FileList empty
Pass Object FileList object, FileList empty
-Fail ImageData 1x1 transparent black
-Fail ImageData 1x1 non-transparent non-black
-Fail Array ImageData object, ImageData 1x1 transparent black
-Fail Array ImageData object, ImageData 1x1 non-transparent non-black
-Fail Object ImageData object, ImageData 1x1 transparent black
-Fail Object ImageData object, ImageData 1x1 non-transparent non-black
+Pass ImageData 1x1 transparent black
+Pass ImageData 1x1 non-transparent non-black
+Pass Array ImageData object, ImageData 1x1 transparent black
+Pass Array ImageData object, ImageData 1x1 non-transparent non-black
+Pass Object ImageData object, ImageData 1x1 transparent black
+Pass Object ImageData object, ImageData 1x1 non-transparent non-black
Pass Array sparse
Pass Array with non-index property
Pass Object with index property and length
diff --git a/Tests/LibWeb/Text/input/HTML/StructuredClone-serializable-objects.html b/Tests/LibWeb/Text/input/HTML/StructuredClone-serializable-objects.html
index f7aa39ef71d..cf1b8625c82 100644
--- a/Tests/LibWeb/Text/input/HTML/StructuredClone-serializable-objects.html
+++ b/Tests/LibWeb/Text/input/HTML/StructuredClone-serializable-objects.html
@@ -66,6 +66,24 @@
println(`instanceOf DOMException: ${domException instanceof DOMException}`);
println(`DOMException: ${domException.message} - ${domException.name}`);
+ const data = new Uint8ClampedArray([
+ 255, 0, 0, 255,
+ 0, 255, 0, 255,
+ 0, 0, 255, 255,
+ 255, 255, 0, 255
+ ]);
+ const imageData = structuredClone(new ImageData(data, 2, 2));
+ println(`instanceOf ImageData: ${imageData instanceof ImageData}`);
+ println(`ImageData.width: ${imageData.width}`);
+ println(`ImageData.height: ${imageData.height}`);
+ for (let i = 0; i < imageData.data.length; i += 4) {
+ const r = imageData.data[i];
+ const g = imageData.data[i + 1];
+ const b = imageData.data[i + 2];
+ const a = imageData.data[i + 3];
+ println(`${r}, ${g}, ${b}, ${a}`);
+ }
+
done();
});