diff --git a/Libraries/LibWeb/HTML/HTMLImageElement.cpp b/Libraries/LibWeb/HTML/HTMLImageElement.cpp
index f1d5ba6cd910..9963b29dfc3d 100644
--- a/Libraries/LibWeb/HTML/HTMLImageElement.cpp
+++ b/Libraries/LibWeb/HTML/HTMLImageElement.cpp
@@ -78,8 +78,13 @@ void HTMLImageElement::adopted_from(DOM::Document& old_document)
if (m_document_observer) {
m_document_observer->set_document(document());
- if (!old_document.is_fully_active() && document().is_fully_active())
- m_document_observer->document_became_active()->function()();
+ if (!old_document.is_fully_active() && document().is_fully_active()) {
+ if (m_document_observer->document_became_active())
+ m_document_observer->document_became_active()->function()();
+ } else if (old_document.is_fully_active() && !document().is_fully_active()) {
+ if (m_document_observer->document_became_inactive())
+ m_document_observer->document_became_inactive()->function()();
+ }
}
}
@@ -336,7 +341,7 @@ String HTMLImageElement::current_src() const
}
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decode
-WebIDL::ExceptionOr> HTMLImageElement::decode() const
+WebIDL::ExceptionOr> HTMLImageElement::decode()
{
auto& realm = this->realm();
@@ -344,89 +349,101 @@ WebIDL::ExceptionOr> HTMLImageElement::decode() const
auto promise = WebIDL::create_promise(realm);
// 2. Queue a microtask to perform the following steps:
- queue_a_microtask(&document(), GC::create_function(realm.heap(), [this, promise, &realm]() mutable {
+ queue_a_microtask(&document(), GC::create_function(realm.heap(), [this, promise, &realm] mutable {
// 1. Let global be this's relevant global object.
auto& global = relevant_global_object(*this);
- 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"_utf16);
+ auto reject_promise = GC::create_function(realm.heap(), [promise, &realm](Utf16String message) {
+ auto exception = WebIDL::EncodingError::create(realm, message);
HTML::TemporaryExecutionContext context(realm);
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"_utf16);
- HTML::TemporaryExecutionContext context(realm);
- WebIDL::reject_promise(realm, promise, exception);
- return true;
- };
+ });
// 2. If any of the following are true:
// - this's node document is not fully active;
// - or this's current request's state is broken,
// then reject promise with an "EncodingError" DOMException.
- if (reject_if_document_not_fully_active() || reject_if_current_request_state_broken()) {
+ if (!this->document().is_fully_active()) {
+ reject_promise->function()("Node document is not fully active"_utf16);
return;
}
+ if (this->current_request().state() == ImageRequest::State::Broken) {
+ reject_promise->function()("Current request state is broken"_utf16);
+ return;
+ }
+
+ auto queue_reject_task = GC::create_function(realm.heap(), [&realm, &global, reject_promise](Utf16String message) {
+ queue_global_task(Task::Source::DOMManipulation, global, GC::create_function(realm.heap(), [reject_promise, message = move(message)] {
+ reject_promise->function()(move(message));
+ }));
+ });
+
// 3. Otherwise, in parallel wait for one of the following cases to occur, and perform the corresponding actions:
- Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(heap(), [this, promise, &realm, &global] {
- Platform::EventLoopPlugin::the().spin_until(GC::create_function(heap(), [this, promise, &realm, &global] {
- auto queue_reject_task = [promise, &realm, &global](Utf16String message) {
- queue_global_task(Task::Source::DOMManipulation, global, GC::create_function(realm.heap(), [&realm, promise, message = move(message)] {
- auto exception = WebIDL::EncodingError::create(realm, message);
- HTML::TemporaryExecutionContext context(realm);
- WebIDL::reject_promise(realm, promise, exception);
- }));
- };
-
- // -> This img element's node document stops being fully active
- if (!document().is_fully_active()) {
- // Queue a global task on the DOM manipulation task source with global to reject promise with an "EncodingError" DOMException.
- queue_reject_task("Node document not fully active"_utf16);
- return true;
- }
+ auto check_decoding_state = GC::create_function(realm.heap(), [this, queue_reject_task, &global, &realm, promise] {
+ // -> This img element's node document stops being fully active
+ if (!document().is_fully_active()) {
+ // Queue a global task on the DOM manipulation task source with global to reject promise with an "EncodingError" DOMException.
+ queue_reject_task->function()("Node document not fully active"_utf16);
+ return true;
+ }
- auto state = this->current_request().state();
+ // -> FIXME: This img element's current request changes or is mutated
+ if (false) {
+ // Queue a global task on the DOM manipulation task source with global to reject promise with an "EncodingError" DOMException.
+ queue_reject_task->function()("Current request changed or was mutated"_utf16);
+ return true;
+ }
- // -> FIXME: This img element's current request changes or is mutated
- if (false) {
- // Queue a global task on the DOM manipulation task source with global to reject promise with an "EncodingError" DOMException.
- queue_reject_task("Current request changed or was mutated"_utf16);
- return true;
- }
+ auto state = this->current_request().state();
- // -> This img element's current request's state becomes broken
- if (state == ImageRequest::State::Broken) {
- // Queue a global task on the DOM manipulation task source with global to reject promise with an "EncodingError" DOMException.
- queue_reject_task("Current request state is broken"_utf16);
- return true;
- }
+ // -> This img element's current request's state becomes broken
+ if (state == ImageRequest::State::Broken) {
+ // Queue a global task on the DOM manipulation task source with global to reject promise with an "EncodingError" DOMException.
+ queue_reject_task->function()("Current request state is broken"_utf16);
+ return true;
+ }
+
+ // -> This img element's current request's state becomes completely available
+ if (state == ImageRequest::State::CompletelyAvailable) {
+ // FIXME: Decode the image.
+ // FIXME: If decoding does not need to be performed for this image (for example because it is a vector graphic) or the decoding process completes successfully, then queue a global task on the DOM manipulation task source with global to resolve promise with undefined.
+ // FIXME: If decoding fails (for example due to invalid image data), then queue a global task on the DOM manipulation task source with global to reject promise with an "EncodingError" DOMException.
+
+ // NOTE: For now we just resolve it.
+ queue_global_task(Task::Source::DOMManipulation, global, GC::create_function(realm.heap(), [&realm, promise] {
+ HTML::TemporaryExecutionContext context(realm);
+ WebIDL::resolve_promise(realm, promise, JS::js_undefined());
+ }));
+ return true;
+ }
+
+ return false;
+ });
- // -> This img element's current request's state becomes completely available
- if (state == ImageRequest::State::CompletelyAvailable) {
- // FIXME: Decode the image.
- // FIXME: If decoding does not need to be performed for this image (for example because it is a vector graphic) or the decoding process completes successfully, then queue a global task on the DOM manipulation task source with global to resolve promise with undefined.
- // FIXME: If decoding fails (for example due to invalid image data), then queue a global task on the DOM manipulation task source with global to reject promise with an "EncodingError" DOMException.
-
- // NOTE: For now we just resolve it.
- queue_global_task(Task::Source::DOMManipulation, global, GC::create_function(realm.heap(), [&realm, promise] {
- HTML::TemporaryExecutionContext context(realm);
- WebIDL::resolve_promise(realm, promise, JS::js_undefined());
- }));
- return true;
+ bool currently_ready = check_decoding_state->function()();
+ if (!currently_ready) {
+ auto check_state_callback = GC::create_function(realm.heap(), [this, check_decoding_state] {
+ bool ready_now = check_decoding_state->function()();
+ if (ready_now) {
+ this->current_request().set_state_changed_callback(nullptr);
+ m_document_observer->set_document_became_inactive({});
}
+ });
- return false;
- }));
- }));
+ // FIXME: These should really be add, not set
+ this->current_request().set_state_changed_callback(check_state_callback);
+
+ if (!m_document_observer)
+ m_document_observer = realm.create(realm, document());
+
+ if (!m_document_observer->document_became_inactive()) {
+ // FIXME: These should really be add, not set
+ m_document_observer->set_document_became_inactive([check_state_callback] {
+ check_state_callback->function()();
+ });
+ }
+ }
}));
// 3. Return promise.
@@ -506,10 +523,9 @@ void HTMLImageElement::update_the_image_data(bool restart_animations, bool maybe
// 2. Wait until the element's node document is fully active.
// 3. If another instance of this algorithm for this img element was started after this instance
// (even if it aborted and is no longer running), then return.
- if (m_document_observer)
- return;
+ if (!m_document_observer)
+ m_document_observer = realm.create(realm, document());
- m_document_observer = realm.create(realm, document());
m_document_observer->set_document_became_active([this, restart_animations, maybe_omit_events]() {
// 4. Queue a microtask to continue this algorithm.
queue_a_microtask(&document(), GC::create_function(this->heap(), [this, restart_animations, maybe_omit_events]() {
diff --git a/Libraries/LibWeb/HTML/HTMLImageElement.h b/Libraries/LibWeb/HTML/HTMLImageElement.h
index 28dd9e2d12fa..fa651387dd26 100644
--- a/Libraries/LibWeb/HTML/HTMLImageElement.h
+++ b/Libraries/LibWeb/HTML/HTMLImageElement.h
@@ -68,7 +68,7 @@ class HTMLImageElement final
String current_src() const;
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decode
- [[nodiscard]] WebIDL::ExceptionOr> decode() const;
+ [[nodiscard]] WebIDL::ExceptionOr> decode();
virtual Optional default_role() const override;
diff --git a/Libraries/LibWeb/HTML/ImageRequest.cpp b/Libraries/LibWeb/HTML/ImageRequest.cpp
index b1cc275f8c1a..77e49f00ff62 100644
--- a/Libraries/LibWeb/HTML/ImageRequest.cpp
+++ b/Libraries/LibWeb/HTML/ImageRequest.cpp
@@ -44,6 +44,7 @@ void ImageRequest::visit_edges(JS::Cell::Visitor& visitor)
visitor.visit(m_shared_resource_request);
visitor.visit(m_page);
visitor.visit(m_image_data);
+ visitor.visit(m_state_change_callback);
}
// https://html.spec.whatwg.org/multipage/images.html#img-available
@@ -66,6 +67,9 @@ ImageRequest::State ImageRequest::state() const
void ImageRequest::set_state(State state)
{
m_state = state;
+
+ if (m_state_change_callback)
+ m_state_change_callback->function()();
}
void ImageRequest::set_current_url(JS::Realm& realm, String url)
@@ -134,4 +138,9 @@ void ImageRequest::add_callbacks(Function on_finish, Function on
m_shared_resource_request->add_callbacks(move(on_finish), move(on_fail));
}
+void ImageRequest::set_state_changed_callback(GC::Ptr> callback)
+{
+ m_state_change_callback = callback;
+}
+
}
diff --git a/Libraries/LibWeb/HTML/ImageRequest.h b/Libraries/LibWeb/HTML/ImageRequest.h
index 8de4f6fa5099..fe9e8c68e96a 100644
--- a/Libraries/LibWeb/HTML/ImageRequest.h
+++ b/Libraries/LibWeb/HTML/ImageRequest.h
@@ -56,6 +56,7 @@ class ImageRequest final : public JS::Cell {
void fetch_image(JS::Realm&, GC::Ref);
void add_callbacks(Function on_finish, Function on_fail);
+ void set_state_changed_callback(GC::Ptr> callback);
GC::Ptr shared_resource_request() const { return m_shared_resource_request; }
@@ -87,6 +88,8 @@ class ImageRequest final : public JS::Cell {
Optional m_preferred_density_corrected_dimensions;
GC::Ptr m_shared_resource_request;
+
+ GC::Ptr> m_state_change_callback;
};
// https://html.spec.whatwg.org/multipage/images.html#abort-the-image-request