diff --git a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp
index 88375b0e012..0ca23ac73d8 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp
+++ b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp
@@ -26,9 +26,11 @@
#include
#include
#include
+#include
#include
#include
#include
+#include
#include
#include
@@ -276,6 +278,86 @@ String HTMLImageElement::current_src() const
return MUST(m_current_request->current_url().to_string());
}
+// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decode
+WebIDL::ExceptionOr> HTMLImageElement::decode() const
+{
+ auto& realm = this->realm();
+
+ // 1. Let promise be a new promise.
+ auto promise = WebIDL::create_promise(realm);
+
+ // 2. Queue a microtask to perform the following steps:
+ queue_a_microtask(&document(), JS::create_heap_function(realm.heap(), [this, promise, &realm]() mutable {
+ auto reject_if_document_not_fully_active = [this, promise, &realm]() -> bool {
+ if (this->document().is_fully_active())
+ return false;
+
+ auto exception = WebIDL::EncodingError::create(realm, "Node document not fully active"_fly_string);
+ WebIDL::reject_promise(realm, promise, exception);
+ return true;
+ };
+
+ auto reject_if_current_request_state_broken = [this, promise, &realm]() {
+ if (this->current_request().state() != ImageRequest::State::Broken)
+ return false;
+
+ auto exception = WebIDL::EncodingError::create(realm, "Current request state is broken"_fly_string);
+ WebIDL::reject_promise(realm, promise, exception);
+ return true;
+ };
+
+ // 2.1 If any of the following are true:
+ // 2.1.1 this's node document is not fully active;
+ // 2.1.1 then reject promise with an "EncodingError" DOMException.
+ if (reject_if_document_not_fully_active())
+ return;
+
+ // 2.1.2 or this's current request's state is broken,
+ // 2.1.2 then reject promise with an "EncodingError" DOMException.
+ if (reject_if_current_request_state_broken())
+ return;
+
+ // 2.2 Otherwise, in parallel wait for one of the following cases to occur, and perform the corresponding actions:
+ Platform::EventLoopPlugin::the().deferred_invoke([this, promise, &realm, reject_if_document_not_fully_active, reject_if_current_request_state_broken] {
+ Platform::EventLoopPlugin::the().spin_until([&] {
+ auto state = this->current_request().state();
+
+ return !this->document().is_fully_active() || state == ImageRequest::State::Broken || state == ImageRequest::State::CompletelyAvailable;
+ });
+
+ // 2.2.1 This img element's node document stops being fully active
+ // 2.2.1 Reject promise with an "EncodingError" DOMException.
+ if (reject_if_document_not_fully_active())
+ return;
+
+ // FIXME: 2.2.2 This img element's current request changes or is mutated
+ // FIXME: 2.2.2 Reject promise with an "EncodingError" DOMException.
+
+ // 2.2.3 This img element's current request's state becomes broken
+ // 2.2.3 Reject promise with an "EncodingError" DOMException.
+ if (reject_if_current_request_state_broken())
+ return;
+
+ // 2.2.4 This img element's current request's state becomes completely available
+ if (this->current_request().state() == ImageRequest::State::CompletelyAvailable) {
+ // 2.2.4.1 FIXME: Decode the image.
+ // 2.2.4.2 FIXME: If decoding does not need to be performed for this image (for example because it is a vector graphic), resolve promise with undefined.
+ // 2.2.4.3 FIXME: If decoding fails (for example due to invalid image data), reject promise with an "EncodingError" DOMException.
+ // 2.2.4.4 FIXME: If the decoding process completes successfully, resolve promise with undefined.
+ // 2.2.4.5 FIXME: User agents should ensure that the decoded media data stays readily available until at least the end of the next successful update
+ // the rendering step in the event loop. This is an important part of the API contract, and should not be broken if at all possible.
+ // (Typically, this would only be violated in low-memory situations that require evicting decoded image data, or when the image is too large
+ // to keep in decoded form for this period of time.)
+
+ HTML::TemporaryExecutionContext execution_context { Bindings::host_defined_environment_settings_object(realm) };
+ WebIDL::resolve_promise(realm, promise, JS::js_undefined());
+ }
+ });
+ }));
+
+ return JS::NonnullGCPtr { verify_cast(*promise->promise()) };
+}
+
Optional HTMLImageElement::default_role() const
{
// https://www.w3.org/TR/html-aria/#el-img
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.h b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.h
index 5ef975c7945..668b16b1fd9 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.h
+++ b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.h
@@ -60,6 +60,9 @@ public:
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-currentsrc
String current_src() const;
+ // https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decode
+ [[nodiscard]] WebIDL::ExceptionOr> decode() const;
+
virtual Optional default_role() const override;
// https://html.spec.whatwg.org/multipage/images.html#img-environment-changes
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.idl
index 94be8936729..209d682d064 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.idl
+++ b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.idl
@@ -25,7 +25,7 @@ interface HTMLImageElement : HTMLElement {
[CEReactions, Enumerated=LazyLoadingAttribute, Reflect] attribute DOMString loading;
[CEReactions, Enumerated=FetchPriorityAttribute, Reflect=fetchpriority] attribute DOMString fetchPriority;
- [FIXME] Promise decode();
+ Promise decode();
// Obsolete
[CEReactions, Reflect] attribute DOMString name;