From 81ccb655b4520fb388e10941a995f61960e50300 Mon Sep 17 00:00:00 2001 From: ayeteadoe Date: Sun, 22 Jun 2025 01:15:23 -0700 Subject: [PATCH] LibWeb: Implement HTML::ImageBitmap creation from HTML::ImageData --- Libraries/LibWeb/HTML/EventLoop/Task.h | 3 + .../LibWeb/HTML/WindowOrWorkerGlobalScope.cpp | 73 ++++++++++++++++--- ...bitmap-from-detached-image-data-buffer.txt | 1 + .../HTML/image-bitmap-from-image-data.txt | 1 + ...age-bitmap-from-invalid-types-no-crash.txt | 6 +- ...itmap-from-detached-image-data-buffer.html | 25 +++++++ .../HTML/image-bitmap-from-image-data.html | 25 +++++++ 7 files changed, 120 insertions(+), 14 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/HTML/image-bitmap-from-detached-image-data-buffer.txt create mode 100644 Tests/LibWeb/Text/expected/HTML/image-bitmap-from-image-data.txt create mode 100644 Tests/LibWeb/Text/input/HTML/image-bitmap-from-detached-image-data-buffer.html create mode 100644 Tests/LibWeb/Text/input/HTML/image-bitmap-from-image-data.html diff --git a/Libraries/LibWeb/HTML/EventLoop/Task.h b/Libraries/LibWeb/HTML/EventLoop/Task.h index 7775fd9c18f..e2bd1a68707 100644 --- a/Libraries/LibWeb/HTML/EventLoop/Task.h +++ b/Libraries/LibWeb/HTML/EventLoop/Task.h @@ -38,6 +38,9 @@ public: // https://w3c.github.io/geolocation/#dfn-geolocation-task-source Geolocation, + // https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#bitmap-task-source + BitmapTask, + // https://html.spec.whatwg.org/multipage/webappapis.html#navigation-and-traversal-task-source NavigationAndTraversal, diff --git a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp index ea0af065499..096eedbea21 100644 --- a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp +++ b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -120,7 +121,7 @@ GC::Ref WindowOrWorkerGlobalScopeMixin::create_image_bitmap(Ima return create_image_bitmap_impl(image, sx, sy, sw, sh, options); } -GC::Ref WindowOrWorkerGlobalScopeMixin::create_image_bitmap_impl(ImageBitmapSource& image, Optional sx, Optional sy, Optional sw, Optional sh, Optional& options) const +GC::Ref WindowOrWorkerGlobalScopeMixin::create_image_bitmap_impl(ImageBitmapSource& image, [[maybe_unused]] Optional sx, [[maybe_unused]] Optional sy, Optional sw, Optional sh, Optional& options) const { auto& realm = this_impl().realm(); @@ -192,26 +193,76 @@ GC::Ref WindowOrWorkerGlobalScopeMixin::create_image_bitmap_imp // If this is an animated image, imageBitmap's bitmap data must only be taken from the default image // of the animation (the one that the format defines is to be used when animation is not supported // or is disabled), or, if there is no such image, the first frame of the animation. + // FIXME: Actually crop the image to the source rectangle with formatting: https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#cropped-to-the-source-rectangle-with-formatting image_bitmap->set_bitmap(result.frames.take_first().bitmap); auto& realm = relevant_realm(p->promise()); - // 5. Resolve p with imageBitmap. - TemporaryExecutionContext context { relevant_realm(*image_bitmap), TemporaryExecutionContext::CallbacksEnabled::Yes }; - WebIDL::resolve_promise(realm, *p, image_bitmap); + // 5. Queue a global task, using the bitmap task source, to resolve promise with imageBitmap. + queue_global_task(Task::Source::BitmapTask, *image_bitmap, GC::create_function(realm.heap(), [p, image_bitmap] { + auto& realm = relevant_realm(*image_bitmap); + TemporaryExecutionContext const context { realm, TemporaryExecutionContext::CallbacksEnabled::Yes }; + WebIDL::resolve_promise(realm, *p, image_bitmap); + })); return {}; }; (void)Web::Platform::ImageCodecPlugin::the().decode_image(image_data, move(on_successful_decode), move(on_failed_decode)); })); }, - [&](auto&) { - dbgln("(STUBBED) createImageBitmap() for non-blob types"); - (void)sx; - (void)sy; - auto error = JS::Error::create(realm, "Not Implemented: createImageBitmap() for non-blob types"sv); - TemporaryExecutionContext context { relevant_realm(p->promise()), TemporaryExecutionContext::CallbacksEnabled::Yes }; - WebIDL::reject_promise(realm, *p, error); + [&](GC::Root const& image_data) -> void { + // 1. Let buffer be image's data attribute value's [[ViewedArrayBuffer]] internal slot. + auto const buffer = image_data->data()->viewed_array_buffer(); + + // 2. If IsDetachedBuffer(buffer) is true, then return a promise rejected with an "InvalidStateError" DOMException. + if (buffer->is_detached()) { + WebIDL::reject_promise(realm, *p, WebIDL::InvalidStateError::create(image_bitmap->realm(), "Image data is detached"_string)); + return; + } + + // 3. Set imageBitmap's bitmap data to image's image data, cropped to the source rectangle with formatting. + // FIXME: Actually crop the image to the source rectangle with formatting: https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#cropped-to-the-source-rectangle-with-formatting + image_bitmap->set_bitmap(image_data->bitmap()); + + // 4. Queue a global task, using the bitmap task source, to resolve promise with imageBitmap. + queue_global_task(Task::Source::BitmapTask, image_bitmap, GC::create_function(realm.heap(), [p, image_bitmap] { + auto& realm = relevant_realm(image_bitmap); + TemporaryExecutionContext const context { realm, TemporaryExecutionContext::CallbacksEnabled::Yes }; + WebIDL::resolve_promise(realm, *p, image_bitmap); + })); + }, + [&](CanvasImageSource const& image_source) { + image_source.visit( + [&](GC::Root const&) { + dbgln("(STUBBED) createImageBitmap() for HTMLImageElement"); + auto const error = JS::Error::create(realm, "Not Implemented: createImageBitmap() for HTMLImageElement"sv); + TemporaryExecutionContext const context { relevant_realm(p->promise()), TemporaryExecutionContext::CallbacksEnabled::Yes }; + WebIDL::reject_promise(realm, *p, error); + }, + [&](GC::Root const&) { + dbgln("(STUBBED) createImageBitmap() for SVGImageElement"); + auto const error = JS::Error::create(realm, "Not Implemented: createImageBitmap() for SVGImageElement"sv); + TemporaryExecutionContext const context { relevant_realm(p->promise()), TemporaryExecutionContext::CallbacksEnabled::Yes }; + WebIDL::reject_promise(realm, *p, error); + }, + [&](GC::Root const&) { + dbgln("(STUBBED) createImageBitmap() for HTMLCanvasElement"); + auto const error = JS::Error::create(realm, "Not Implemented: createImageBitmap() for HTMLCanvasElement"sv); + TemporaryExecutionContext const context { relevant_realm(p->promise()), TemporaryExecutionContext::CallbacksEnabled::Yes }; + WebIDL::reject_promise(realm, *p, error); + }, + [&](GC::Root const&) { + dbgln("(STUBBED) createImageBitmap() for ImageBitmap"); + auto const error = JS::Error::create(realm, "Not Implemented: createImageBitmap() for ImageBitmap"sv); + TemporaryExecutionContext const context { relevant_realm(p->promise()), TemporaryExecutionContext::CallbacksEnabled::Yes }; + WebIDL::reject_promise(realm, *p, error); + }, + [&](GC::Root const&) { + dbgln("(STUBBED) createImageBitmap() for HTMLVideoElement"); + auto const error = JS::Error::create(realm, "Not Implemented: createImageBitmap() for HTMLVideoElement"sv); + TemporaryExecutionContext const context { relevant_realm(p->promise()), TemporaryExecutionContext::CallbacksEnabled::Yes }; + WebIDL::reject_promise(realm, *p, error); + }); }); // 7. Return p. diff --git a/Tests/LibWeb/Text/expected/HTML/image-bitmap-from-detached-image-data-buffer.txt b/Tests/LibWeb/Text/expected/HTML/image-bitmap-from-detached-image-data-buffer.txt new file mode 100644 index 00000000000..abc2bfe8b66 --- /dev/null +++ b/Tests/LibWeb/Text/expected/HTML/image-bitmap-from-detached-image-data-buffer.txt @@ -0,0 +1 @@ +Image data is detached diff --git a/Tests/LibWeb/Text/expected/HTML/image-bitmap-from-image-data.txt b/Tests/LibWeb/Text/expected/HTML/image-bitmap-from-image-data.txt new file mode 100644 index 00000000000..9b7fa9ecfd9 --- /dev/null +++ b/Tests/LibWeb/Text/expected/HTML/image-bitmap-from-image-data.txt @@ -0,0 +1 @@ +[Success]: [object ImageBitmap] diff --git a/Tests/LibWeb/Text/expected/HTML/image-bitmap-from-invalid-types-no-crash.txt b/Tests/LibWeb/Text/expected/HTML/image-bitmap-from-invalid-types-no-crash.txt index e9d593bdc3f..a4031938dc4 100644 --- a/Tests/LibWeb/Text/expected/HTML/image-bitmap-from-invalid-types-no-crash.txt +++ b/Tests/LibWeb/Text/expected/HTML/image-bitmap-from-invalid-types-no-crash.txt @@ -1,7 +1,7 @@ Blob [Success]: [object ImageBitmap] -ImageData [ Error ]: Error: Not Implemented: createImageBitmap() for non-blob types +ImageData [Success]: [object ImageBitmap] HTMLImageElement [ Error ]: InvalidStateError: image argument is not usable SVGImageElement [ Error ]: InvalidStateError: image argument is not usable -HTMLCanvasElement [ Error ]: Error: Not Implemented: createImageBitmap() for non-blob types +HTMLCanvasElement [ Error ]: Error: Not Implemented: createImageBitmap() for HTMLCanvasElement ImageBitmap [ Error ]: TypeError: No union types matched -HTMLVideoElement [ Error ]: InvalidStateError: image argument is not usable +HTMLVideoElement [ Error ]: InvalidStateError: image argument is not usable \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/HTML/image-bitmap-from-detached-image-data-buffer.html b/Tests/LibWeb/Text/input/HTML/image-bitmap-from-detached-image-data-buffer.html new file mode 100644 index 00000000000..a0adaa519f3 --- /dev/null +++ b/Tests/LibWeb/Text/input/HTML/image-bitmap-from-detached-image-data-buffer.html @@ -0,0 +1,25 @@ + + + diff --git a/Tests/LibWeb/Text/input/HTML/image-bitmap-from-image-data.html b/Tests/LibWeb/Text/input/HTML/image-bitmap-from-image-data.html new file mode 100644 index 00000000000..d21f76ef5d7 --- /dev/null +++ b/Tests/LibWeb/Text/input/HTML/image-bitmap-from-image-data.html @@ -0,0 +1,25 @@ + + + + +