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;